Timing + randomization

59 views
Skip to first unread message

Benedek Kurdi

unread,
Mar 26, 2021, 11:25:44 AM3/26/21
to Minno.js
Hi all,
I have two quick questions. Hopefully neither of them will be too complicated.
The first one has to do with the timing of some material that I want participants to read. The goal would be to create the page in such a way that the "Continue" button appears only after a certain amount of time to force participants to spend a certain amount of time reading before they can proceed. I tried doing the following:
define(['questAPI'], function(Quest){
    var API = new Quest();

    API.addSequence([
        { // page begins
            header: false,
            timer: {
                duration: 5,
                show: false
            },
            questions: [
                {
                 type: 'info',
                 description: '<p class="lead">'+
                 '<br/><br/><img src="/implicit/user/bkurdi/reinterpret1a/images/' + API.getGlobal().target1Photo[0] + '.jpg" height="250">'+
                 '<br/><br/>You are about to read one more piece of information about Francis West on the next page.'+
                 '<br/><br/>Click “Continue” to begin. You must wait at least 5 seconds to proceed.'+
                 '<br/><br/>'
                } // question ends
            ],
            noSubmit: false,
            submitText: 'Continue'
        } // page ends

    ]);
    
    return API.script;
});
What this does though is that the "Continue" button appears immediately and the page automatically proceeds after 5 seconds (if the participant hasn't clicked "Continue" already). How can I fix this?
The second question has to do with randomization. I have a study that will have a two-day time delay. I have to make sure that stimuli are randomized the same way on day 1 and day 3. To this end, I'm using a function that Elad wrote for me where the participant enters the same six-digit number on both days and that number is used to set the random seed. In previous studies, I used this function in one specific task. However, now  I want to randomize stimuli in the manager file because I want the stimuli to be available across the entire study. Unfortunately Minno is yelling at me (and rightfully so) when it gets to this line of code in the manager file:
    var rand = generatePRNG(global.seed.questions.year.response);
The reason is that this variable is not yet defined at this point because the participant hasn't entered anything. Is there a way I can define this variable with some arbitrary value at the beginning of the study and then have it be overwritten with whatever the participant has entered once they get to the "seed" task?
Thanks so much in advance!
—Benedek

Mayan Navon

unread,
Mar 27, 2021, 10:55:01 AM3/27/21
to Benedek Kurdi, Minno.js
Hi Benedek,

1. Perhaps there's a more elegant solution, but here is a quick way of doing this:
(I switched to an actual question because the onLoad hook didn't work with the info question). 
define(['questAPI'], function(quest){

    var API = new quest();

    /**
    Question-prototypes
    **/
    API.addQuestionsSet('basicSelect',
    {
        type: 'selectOne',
        style:'multiButtons',
        autoSubmit:true,
        numericValues:true,
        required:false,
        errorMsg: {
            required: "Please select an answer, or click 'decline to answer'"
        }
    });


// each stim
    API.addQuestionsSet('infoQuest', 
    [
        {
            inherit : 'basicSelect',
            name : 'infoQuest',
            stem :  
                    //'<p class="lead"><br/><br/><img src="/implicit/user/bkurdi/reinterpret1a/images/' + API.getGlobal().target1Photo[0] + '.jpg" height="250">'+
                    '<p class="lead"><br/><br/>You are about to read one more piece of information about Francis West on the next page.'+
                    
'<br/><br/>Click “Continue” to begin. You must wait at least 5 seconds to proceed.'
+
                    '<br/><br/>',
            onLoad: function(log, current) {
            // hide continue button
              document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 0; 
              var delayInMilliseconds = 5000;
              setTimeout(function() {
                  //this code will be executed after 5 second
                  //console.log("done");
                  document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 100; 
                }, delayInMilliseconds);
            } // function ends
        }
    ]);

    /**
    Pages
    **/
    API.addPagesSet('basicPage',
    {
        decline:false,
        v1style:2,
        numbered: false,
        noSubmit:false, //Change to true if you don't want to show the submit button.
        submitText: "Continue"
    });

    /**
    Sequence
    **/
    API.addSequence(
    [
        {inherit : 'basicPage',
        questions : {inherit:{set:'infoQuest'}}}                
    ]);                       
    return API.script;
});
You might need to change the CSS selector (this is the selector I got when running the task from the task's play button, rather than the expt file). For that, you'll need to right-click the Submit button and press inspect. Then, right-click the highlighted line of HTML code, press Copy -> Copy selector.

2. Do you need the stimuli before the seed task? If not, then you can keep the function there, and save 'rand' as global, for later use (in other tasks), with something like this: global.rand = rand.  

Let me know if that helps.

Best,
Mayan


--
You received this message because you are subscribed to the Google Groups "Minno.js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to minnojs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/minnojs/c42f778d-04f8-47fe-9045-7132362543dan%40googlegroups.com.


--
Mayan

Benedek Kurdi

unread,
Mar 28, 2021, 12:44:29 PM3/28/21
to Mayan Navon, Minno.js
1. Perhaps there's a more elegant solution, but here is a quick way of doing this:
(I switched to an actual question because the onLoad hook didn't work with the info question). define(['questAPI'], function(quest){ var API = new quest(); /** Question-prototypes **/ Question-prototypes **/ API.addQuestionsSet('basicSelect', { type: 'selectOne', style:'multiButtons', autoSubmit:true, numericValues:true, required:false, errorMsg: { required: "Please select an answer, or click 'decline to answer'" } }); // each stim API.addQuestionsSet('infoQuest', [ { inherit : 'basicSelect', name : 'infoQuest', stem : //'<p class="lead"><br/><br/><img src="/implicit/user/bkurdi/reinterpret1a/images/' + API.getGlobal().target1Photo[0] + '.jpg" height="250">'+ '<p class="lead"><br/><br/>You are about to read one more piece of information about Francis West on the next page.'+ '<br/><br/>Click “Continue” to begin. You must wait at least 5 seconds to proceed.'+ '<br/><br/>', onLoad: function(log, current) { // hide continue button document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 0; var delayInMilliseconds = 5000; setTimeout(function() { //this code will be executed after 5 second //console.log("done"); document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 100; }, delayInMilliseconds); } // function ends } ]); /** Pages **/ Pages **/ API.addPagesSet('basicPage', { decline:false, v1style:2, numbered: false, noSubmit:false, //Change to true if you don't want to show the submit button. submitText: "Continue" }); /** Sequence **/ Sequence **/ API.addSequence( [ {inherit : 'basicPage', questions : {inherit:{set:'infoQuest'}}} ]); return API.script;});You might need to change the CSS selector (this is the selector I got when running the task from the task's play button, rather than the expt file). For that, you'll need to right-click the Submit button and press inspect. Then, right-click the highlighted line of HTML code, press Copy -> Copy selector.

Unfortunately what I seem to be getting with this is that the "Submit" button is already there but it's graphically invisible. The issue is that if the participant hovers over it, they will be able to see that it's there and proceed before they are supposed to be able to. 

2. Do you need the stimuli before the seed task? If not, then you can keep the function there, and save 'rand' as global, for later use (in other tasks), with something like this: global.rand = rand.  
No, that's a good point. So I guess my question is, would I be able to do all the randomization that I would normally do in the manager file in the file where I get the participant input, and then save the output of the randomization in global variables (the same way I would from the manager file)?
And if so, how do I refer to the "global.seed.questions.year.response" variable if I refer to it from within the same file? I guess global.seed is not necessary, but if I simply omit that, it's not working either.
Thanks so much again!
—Benedek
--
Benedek Kurdi
411 Orange Street #4B
New Haven, CT 06511

Mayan Navon

unread,
Mar 28, 2021, 2:04:19 PM3/28/21
to Benedek Kurdi, Minno.js
1. Right, this should hide the button and its graphics, and block the button's functionality:
// each stim
    API.addQuestionsSet('infoQuest', 
    [
        {
            inherit : 'basicSelect',
            name : 'infoQuest',
            stem :  
                    //'<p class="lead"><br/><br/><img src="/implicit/user/bkurdi/reinterpret1a/images/' + API.getGlobal().target1Photo[0] + '.jpg" height="250">'+
                    '<p class="lead"><br/><br/>You are about to read one more piece of information about Francis West on the next page.'+
                    '<br/><br/>Click “Continue” to begin. You must wait at least 5 seconds to proceed.'+
                    '<br/><br/>',
            onLoad: function(log, current) {
            // hide continue button

              //document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 0;
              document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.visibility = "hidden";
              
var delayInMilliseconds = 5000;
              setTimeout(function() {
                  //this code will be executed after 5 second
                  //console.log("done");

                  //document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.opacity = 100;
              document.querySelector('#task > div > div.ng-scope.ng-isolate-scope > div > div > div.page-buttons').style.visibility = "visible";
                }, delayInMilliseconds);
            } // function ends
        }
    ]);
2. Here is how I would do it: 
I added an onEnd function at the start of the task's script (following the var API = new Quest(); definition), and defined a variable that takes the user's response value. Then, I saved it as a global variable. In your case, you'll need to replace userconsent (the name of the question) with year.
    var API = new Quest();
    var global = API.getGlobal(); 
    var current = API.getCurrent();  

    API.addSettings('onEnd', function()
    {
        var questResp = current.questions.userconsent.response;
        global.questResp = questResp;
        console.log("global.questResp: " + global.questResp);        
    });





--
Mayan

Benedek Kurdi

unread,
Mar 29, 2021, 9:20:22 AM3/29/21
to Mayan Navon, Minno.js

Thanks so much, both are working great now.
—Benedek

Jordan Axt

unread,
Mar 2, 2022, 9:59:33 AM3/2/22
to Benedek Kurdi, Minno.js
Hi Minno Group,

I wanted to follow up on this thread after encountering an issue. The code provided here is working well for delaying the presence of a submit button, but only when there is either a) one page in the .js file, or b) the relevant page is shown first. We are having trouble implementing the delay in a file that has multiple pages.

I linked to an example below. The relevant page is 'shen_eval1_test.js', with the expt file being eroy.JBT_shen.expt.xml:

When the sequence looks like this, it works fine:
 API.addSequence(
    {mixer:'wrapper',data:[
    {inherit:'basicPageDelay',questions:{inherit:{set:'infoQuest'}}},    
    {inherit :'basicPage',questions:{inherit:{set:'jacob1'}}}
    ]});

But if you reverse the order of those two pages, the delay no longer works: 
 API.addSequence(
    {mixer:'wrapper',data:[
     {inherit :'basicPage',questions:{inherit:{set:'jacob1'}}},
    {inherit:'basicPageDelay',questions:{inherit:{set:'infoQuest'}}},    
    ]});

Any idea what the issue may be here? Or of a possible workaround outside of spreading the delay across several files? Thanks for your help and let me know if I can clarify anything here.

Jordan




--
Jordan Axt

Mayan Navon

unread,
Mar 3, 2022, 5:14:59 AM3/3/22
to Jordan Axt, Benedek Kurdi, Minno.js
Hi Jordan,

I think that's because 'jacob1' is an info question and you didn't add the button delay to it (it would work on info questions anyway). So the page loads with the first question, which doesn't have any button-delay code.
The easiest solution would be to use another question type for all info questions, and add the  delay code there as well. Actually, perhaps you can just add that code to the 'basicselect' question you inherit for all questions.

Mayan


Jordan Axt

unread,
Mar 4, 2022, 4:13:06 PM3/4/22
to Mayan Navon, Benedek Kurdi, Minno.js
Hi Mayan,

Thanks for helping me here. I am still having trouble implementing this advice, even on pages that lack an info page at all. Here is another example. The relevant file is lu_intervention1.js and the expt file is eroy.JBT_lu.expt.xml:

This task requires an initial selection and then, depending on the answer selected, the participant is directed to one of two writing prompts. We are trying to create a delay in submitting the writing prompt page. I have added the relevant "onLoad" delay coding to basically every place I could find it in the code, and I still cannot get a delay to show up for the writing prompt. Do you have any idea what the issue may be here?

Thanks,
Jordan
--
Jordan Axt

Mayan Navon

unread,
Mar 7, 2022, 4:14:07 AM3/7/22
to Jordan Axt, Minno.js
Hi Jordan,

So what you need to do is hide the button completely for the first question (e.g., culture) and set a presentation delay for the prompt\text question (e.g., settwo). Here are my edits to your code, and here is an example:

GIF.gif
If there are still any issues, let me know.

Best,
Mayan


On Sun, 6 Mar 2022 at 14:20, Jordan Axt <jorda...@gmail.com> wrote:
Great, just shared with you.

On Sun, Mar 6, 2022 at 7:17 AM Mayan Navon <navm...@gmail.com> wrote:
mayanna



On Sun, 6 Mar 2022 at 14:05, Jordan Axt <jorda...@gmail.com> wrote:
Sure thing! Can you tell me your dashboard username?

On Sun, Mar 6, 2022 at 5:00 AM Mayan Navon <navm...@gmail.com> wrote:
Hi Jordan,

Sorry for the delay in my response. I think that's still because the prompt question is not the first question being loaded.
But, I want to check if I can still make it work. Could you please share your study with me? 

Thanks,
Mayan

Jordan Axt

unread,
Mar 7, 2022, 1:58:06 PM3/7/22
to Mayan Navon, Minno.js
Hi Mayan,

Thank you very much for helping me. Unfortunately I think there was some confusion in my request. I was looking to institute a delay for the text box that occurs after that first page (the question names are 'set1c' and 'set1cm', and the prompt talks about writing for at least five minutes). I apologize for the confusion. I have tried to use your suggestion above and apply it to these items, but I am still not getting anywhere. Would you be able to identify what the issue is?

Best,
Jordan
--
Jordan Axt

Mayan Navon

unread,
Mar 16, 2022, 4:32:24 AM3/16/22
to Minno.js
Here is the solution, by Ronen:

First, you should replace "onLoad" with "onCreate".
If I understand correctly, onLoad happens when the experiment loads (i.e., all the questions are loaded at the same time).
In addition, it seems that there is a synchronization issue that causes it to work with an unavailable ( / undefined) element. 
The easiest solution is by using a short delay before the hiding itself (5 ms should be fine).
The third thing is related to the detection of the element, I think that document.querySelector('[ng-click="submit()"]') will be better here.
To summarize everything, this is how it should be in your code:
            onCreate:function(log, current){
                    setTimeout(function() {
                        document.querySelector('[ng-click="submit()"]').style.visibility = "hidden";

                        var delayInMilliseconds = 5000;
                        setTimeout(function() {
                            document.querySelector('[ng-click="submit()"]').style.visibility = "visible";
                        }, delayInMilliseconds);
                    }, 5);
                }

Reply all
Reply to author
Forward
0 new messages