19 Replies
Ashley Terwilliger-Pollard

Hi Garfield,

I haven't seen any other solutions for this - the text entry is waiting for the user to click outside or onto something else to confirm that they're done typing and continue on. 

It may be something that you could setup using Javascript - so I'd suggest searching ELH for examples using that, or post a new discussion specifically asking for ideas! It's well above my head, so sadly I'm not much help there. 🙃

Jillian Rae

I thought I'd put this here in case anybody finds it useful. This problem was driving me crazy and I didn't like any of the suggested solutions. I solved it using jQuery because I'm still using Storyline 3, but I'm sure you could achieve the same thing in a slightly more long-winded way with straight javascript.

I created an Execute JavaScript trigger to run at timeline start:

$(function() {
  $("input").keyup(function() {
    $(this).blur();
    $(this).focus();
  });
});

Basically it causes the active input element to lose focus very briefly after every keystroke, thereby activating the "control loses focus" trigger, to which you can attach any action you like on a case-by-case basis. In theory the split second loss of focus could cause problems but in practice I have yet to experience any.

Because it works on any unspecified input element you can also put it inside your Slide Master to apply to every slide.

Gavin Elliott

Jillian, you are a life saver. This was also driving me crazy until I came across your post. Thank you so much.

The jQuery didn't work for me for some reason, so I used standard JavaScript. It's below if anyone else needs it.

The '.acc-textinput' is the class name of the text input box in the Storyline module. I'm assuming this is always the same, but if Articulate ever change the name in the future this code won't work.

document.querySelector('.acc-textinput').addEventListener('keyup', () => {
document.querySelector('.acc-textinput').blur();
document.querySelector('.acc-textinput').focus();
});
Gavin Elliott

Hey Curtis and to anyone else who finds themselves here. 

The code below detects when anyone types into any number of text input boxes on a Storyline slide. Whenever a key is pressed the text boxes lose focus for a millisecond and trigger the relevant 'lose focus' trigger in Storyline.

The 'console.log' line can be omitted from the code. If you keep it in, a message is displayed in the browser for testing purposes.

Array.from(document.querySelectorAll('.acc-textinput')).forEach(el => {
    el.addEventListener('keyup', () => {
        console.log('someone is typing in a box...');
        el.blur();
        el.focus();
    })
});

A Storyline file is attached to demonstrate. Simply publish to HTML (web) or SCORM. 

Zsolt Olah

This is neat for just handling the focus. The only thing I would add to double check is accessibility. Normally, a screen reader announces the text input's label when it gets the focus along with the content. Since the code makes the input text field lose focus and then regain focus with each key, make sure the screen reader doesn't automatically reannounce the field. 

Gavin Elliott
Array.from(document.querySelectorAll('.acc-textinput')).forEach(el => {
el.ariaHidden = 'true';
    el.addEventListener('keyup', () => {
       el.blur();
        el.focus();
    })
});

There may be a way to amend the code to take that into account—see above. Setting an element's attribute to 'aria-hidden="true"' should prevent screen readers from announcing anything relating to that element. 

I don't know exactly how Storyline is set up for screenreaders so the above may/may not work. It's worth a try. 

Let us know how you get on.

Glenda DeHoff

You all are rock stars! I know just enough JavaScript to be dangerous, though I love the extensibility it adds to Storyline. Inspired by your code, I wanted to use the contents of the input box to simulate a search box, so I added this.

Array.from(document.querySelectorAll('.acc-textinput')).forEach(el => {
    el.addEventListener('keyup', () => {
        console.log('someone is typing in a box...');
        el.blur();
        el.focus();
    })
});
var player = GetPlayer();
var searchentry = player.GetVar("SearchEntry");
searchentry= Array[1] + Array[2] + Array[3];

player.setVar("SearchEntry",searchentry);

Glenda DeHoff

Hi Curtis,

Variables, in this case, are case-sensitive. I used "SearchEntry" for my Storyline variable and "searchentry" for the JavaScript variable. The first part of the code adds the input to Array, then assigns it to the "searchentry" variable one letter at a time.

Back in Storyline you can test this by outputting the variable to a textbox using %SearchEntry%. After that you can use the variable in anyway you need.  You must publish to test JavaScript.

Good luck!

 

Curtis Stanford

Hi Glenda,

Yep, I understand the case-sensitivity for the the variables. Beyond that, a few issues I'm getting caught up on:

1) Wouldn't you exclude the double quotes around searchentry in player.SetVar?
2) What is triggering player.SetVar? If the execute javascript action is triggered at timeline start, what is then actually causing it to write to SearchEntry?
3) Conceptually, what is the code actually doing? It looks like you're hoping that Array.from is capturing text input, more than just doing blur/focus. Then you create a variable called "searchentry" and set it to "SearchEntry" - which would be blank, right? Why bother? Then you immediately set searchentry to constituent items 2,3,&4 that you're expecting to be captured in Array. Then you output that back out to SearchEntry. If that all worked, wouldn't it just be setting whatever you type in a text input field to SearchEntry? Why not just set the text variable on the input box to SearchEntry? And then once you have data in SearchEntry, how do you use that to search?

In any case, I can't get the code to work.

I think a custom search feature would be super cool! Appreciate any time you're willing to take to help me understand!

Curtis

Glenda DeHoff

You are correct about the double quotes around searchentry in player.SetVar. I edited my post to reflect that.

I was building on Gavin’s excellent work and don’t pretend to understand how his code works. I am certainly not a JavaScript expert. There is probably a more elegant solution. I am creating a software simulation and simply wanted to capture the first few letters of the entry in real-time to trigger a state change from the search page to search results.  For my needs, it doesn’t actually need to work.

Perhaps Gavin or one of the many other JavaScript experts could help you do what you want. I agree - a custom search feature would be super cool! Sorry I couldn’t be of more help.

Gavin Elliott

Hey both. It's great to know this code is generating ideas. :-)

Array.from(document.querySelectorAll('.acc-textinput')).forEach(el => {
    el.addEventListener('keyup', () => {
        console.log('someone is typing in a box...');
        el.blur();
        el.focus();
    })
});

'Blur' and 'focus' only triggers a 'when object loses focus' event in Storyline. It doesn't do anything else. So you must accompany this code with the aforementioned trigger on your slide—otherwise it won't do anything.

The reason why I came up with this code is because the built in trigger in Storyline only triggers ordinarily when a learner clicks on something else on the slide after typing into the box. This is often impractical for creating certain interactions—such as a function where you want to check what someone has typed while they're doing it.

The above code works for a slide where there is one or more text entry boxes. 

If you only had one text entry box, and you wanted to analyse what someone has typed while they are typing it, you could use the code below. It's a type of search I suppose. You wouldn't actually need blur and focus with this example, because we don't need to trigger a 'when object loses focus event in Storyline. 'JavaScript can do the work and simply update a variable in Storyline directly.  

const player = GetPlayer();

document.querySelector('.acc-textinput').addEventListener('keyup', (e) => {
    if (e.target.value.toLowerCase().includes('secret')) {
        player.SetVar('secretFound', true);
    }
});

This code works as follows. Every time a key is pressed, the code runs. It checks what is typed in the TextEntry box by looking for a word or phrase—in this case 'secret'. If it finds it, it sets a variable inside Storyline to 'true' (boolean). A trigger in Storyline makes a layer appear when it detects a change in that particular variable ('when a variable changes'); 

I've attached the Storyline file should you wish to play around with this bit of code. Change the value in quotes after 'includes' to whatever word you want.

You could theoretically do any analysis in JS of what someone has typed and then set a variable in Storyline to trigger something to happen—whatever you want.