Forum Discussion

RichardMulcahy's avatar
RichardMulcahy
Community Member
2 months ago

Any JavaScript Experts Out There?

I found this sample code on the web, but it's not working for me. I'm trying to limit the number of characters a user can enter (for example 5), and if they exceed then I show a layer with an error msg. Here is the code and my SL file. I'm sure it's something simple.

 

var textFields = document.querySelectorAll(‘input[type=”text”]’);
var maxChars = [8, 12];
var player = GetPlayer();

textFields.forEach(function(textField, index) {
    textField.addEventListener(“input”, function() {
        var maxLength = maxChars[index];
        if (textField.value.length > maxLength) {
            textField.value = textField.value.slice(0, maxLength);

            player.SetVar(“ShowLayer”, true);
        } else {

            player.SetVar(“ShowLayer”, false);
        }
    });
});

  • I came across a mutation observer script that will successfully monitor the input value in the current version of Storyline. I inserted it into your original script (the one in the SL file, after fixing the quote character errors), and it worked as expected. 

    The script includes two event handlers; one for user inputs and one for programatic changes to the input value.

    Two things became apparent.

    1. Users input changes don't seem to fire events, but the programatic changes can still be captured.
    2. Your selection method for the inputs creates 4 input references. Two (0 and 1) are under the .acc-shadow-dom class, and do not generate events. The remining two (2 and 3) do generate mutation events. Your maxChars array, as implemented, therefore needs 4 entries.

    The updated script is as follows

    //function below borrowed from 
    //https://stackoverflow.com/questions/32383349/detect-input-value-change-with-mutationobserver#61975440
    
    function observeElement(element, property, callback, delay = 0) {
        let elementPrototype = Object.getPrototypeOf(element);
        if (elementPrototype.hasOwnProperty(property)) {
            let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
            Object.defineProperty(element, property, {
                get: function() {
                    return descriptor.get.apply(this, arguments);
                },
                set: function () {
                    let oldValue = this[property];
                    descriptor.set.apply(this, arguments);
                    let newValue = this[property];
                    if (typeof callback == "function") {
                        setTimeout(callback.bind(this, oldValue, newValue), delay);
                    }
                    return newValue;
                }
            });
        }
    }
    //------------------------------------------------------------------------------
    //Excerpt from your original script
    function inputLengthCheck(textField, index) {
    	var maxLength = maxChars[index];
    	console.log("Idx",index, "maxLen", maxLength);
    	if (textField.value.length > maxLength) {
    		textField.value = textField.value.slice(0, maxLength);
    
    		player.SetVar("ShowLayer", true);
    	} else {
    
    		player.SetVar("ShowLayer", false);
    	}
    }
    //------------------------------------------------------------------------------
    //The body of your original script
    
    var textFields = document.querySelectorAll("input[type='text']");
    
    //Note: in your current selection method, there are actually 4 imput boxes.
    //The two that function with the events are 2 and 3.
    //0 and 1 are part of the shadow-dom (class = acc-shadow=dom)
    var maxChars = [4,4,4,4];
    var player = GetPlayer();
    
    textFields.forEach(function(textField, index) {
    	
    	//Note: this event handler does not get called on user input in Storyline
    	textField.addEventListener("input", function () {
        console.log("Input value changed via UI. New value: '%s'", this.value);
    	
    		//do length check
    		inputLengthCheck(textField, index);
    
    	});
    	
    	//Note: This event handler does get called in Storyline
    	observeElement(textField, "value", function (oldValue, newValue) {
        console.log("Input value changed via API. Value changed from '%s' to '%s'", oldValue, newValue);
    
    		//do length check
    		inputLengthCheck(textField, index);
    
    	});
    });

     

  • A few things going on here. The first is with the quote characters those are throwing an error. Unless it's in a code field, stuff off the internet can frequently use the wrong characters. JavaScript is literal and will completely bomb if a character is out of place. 

    An easy way to check for errors is to preview, then use the Inspect feature. Scroll to the top of the DevTools window and if there's an error in user.js, it's a syntax problem. However, even after resolving that problem, the script doesn't do what you'd hope it would do. I don't think the method the site where the script came from works, at least not with the latest version of SL. 

    Logical field connections appear abstracted. There's the visual representation of the text box and another representation of the text block behind that. There is likely some accessibility reason for this. Hard to tell. I added a console log to show the text fields on the screen with just the two text blocks below.

    There is a timing issue happening somewhere in the stack. Operating off of the variable alone in JS won't help because the value isn't committed until after the text box is left. Your best bet will likely be using a webobject that contains all of your textbox limits and interactions. You can link this webobject to your SL variables using Javascript.

     

  • As Steve mentioned copying from the Internet often gives errors related to brackets and quotes/smartquotes. Fixing these aint enough as there are more issues. No direct need however to use WebObjects as you can manage this directly in Storyline. As shown here.
    https://360.articulate.com/review/content/00cd0507-bdc1-426d-8335-31b19ca905bf/review

    Your original file however does have some weird behaviour when checking the browser console. As seen below:

    All of these errors are Articulate Storyline errors... in slides.min.js so we cannot fix those. I however remade the project ( as seen in the link above and in the attached file ) and none of these errors show up then.

    Kind regards,
    Math

    PS. As Steve mentioned the check is only triggered after leaving focus of a textfield.
    I do think i have a solution for that somewhere...where each entry of a character is checked.

  • As if found the code for checking per character entry and wanted to add that to this file... i noticed i got the same errors i got from your original ( as shown in the screenshot above ). So this for sure is an bug in the latest version of Storyline. Older files work flawless but when edited in the newest version...this happens.

    • RichardMulcahy's avatar
      RichardMulcahy
      Community Member

      Thanks Math, that does work!

      Is there a way to ShowLayer when the user exceeds the limit without having the text box loose focus?

  • Seb-Daubert's avatar
    Seb-Daubert
    Community Member

    Hello, the structure of the editable text fields has recently been modified by articulate for export, I had developed several scripts to limit the number of characters, limit the type of characters (uppercase, lowercase, numeric, ect...) , it no longer works today.
    We can get around the problem in a fairly laborious way, we have to create a rectangular shape in storyline which will act as a receptacle for a standard HTML text field, I dynamically create a standard text field in JavaScript and apply the desired restrictions to it, and I ensures that the text field is always the same size and position as my container.
    In addition you need a second javascript to delete it if the slide change :(
    I don't know why the developers decided to make this change to the text fields, it would be nice to have a follow-up on the structural modifications of the export elements...

    • MathNotermans-9's avatar
      MathNotermans-9
      Community Member

      :-) One of the major lack in Articulate development. Lack of openness on this. The times something old didnot work in the newest version due to undocumented changes on Articulate side ;-(

      • RichardMulcahy's avatar
        RichardMulcahy
        Community Member

        That's great and thanks.  I also only need it for one text box on a slide.

  • Thanks everyone for their feedback, I really appreciate the time and effort you all took.

  • Was seeing similar symptoms. Because of the way fields are handled, I'm not sure it's possible to rewrite field contents because of the field construction as Seb mentions above. While it monitors the length of entry, the script to constrain the length wasn't working. The simplest solution, to me, is going to a web object. Nice and clean.