Ensure the user clicks on a given set of words before continuing

PennController for IBEX Forums Support Ensure the user clicks on a given set of words before continuing

Viewing 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • #6704
    candido.otero
    Participant

    Hi,

    I want to have the user click on a given set of target words among all the words appearing on the screen before moving on to the next step of the trial (or maybe finish the trial).

    My best attempt was to create a button for each word. If this is a target word, I disable the button and mark it in red. I wanted to create a variable to count the number of target words that have been clicked and move on when the counter equals the number of target words.

    In the example below I have 8 words (‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’) and 3 target words (‘b’, ‘e’, ‘h’).

    I am having trouble implementing the counter, as I get an error if the value I initialise the variable to is not a string (e.g., newVar("counterClicked", 0) breaks the execution).

    Another thing I would like to display is the number of words left to click (e.g., 2 words left to click). So far, I have failed to update the text that contains a variable.

    The code I have so far is:

    PennController.ResetPrefix(null) // Keep here
    
    /**
     * Shuffles array in place.
     * @param {Array} a items An array containing the items.
     */
    function shuffle(a) {
        var j, x, i;
        for (i = a.length - 1; i > 0; i--) {
            j = Math.floor(Math.random() * (i + 1));
            x = a[i];
            a[i] = a[j];
            a[j] = x;
        }
        return a;
    }
    
    const all = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
    var sample = ['b', 'e', 'h'];
    var canvasWords = newCanvas("words canvas", 800, 400);
    
    function generateButtons(allWords, targetWords, canvas) {
        var trialElems = [];
        var rndAllWords = [...allWords];
        shuffle(rndAllWords);
        rndAllWords.forEach((word, idx) => {
            var pos_Y = Math.floor(idx / 4);
            var pos_X = idx % 4;
            
            if (targetWords.includes(word)) {
                trialElems.push(
                    newButton('button'+word, word)
                        .callback(getButton('button'+word).settings.color('red'))
                        .callback(getButton('button'+word).disable())
                );
            } else {
                trialElems.push(
                    newButton('button'+word, word)
                );
            }
            
            canvas = canvas.add(pos_X * 200, pos_Y * 40, getButton('button'+word));
        });
        
        return trialElems;
    }
    
    newTrial('test',
        newVar("counterClicked", 0)
        ,
        ...generateButtons(all, sample, canvasWords)
        ,
        newText('counterText', getVar('counterClicked')).print()
        , 
        canvasWords
            .print()
            .wait()
    );

    So the expected behaviour is that after clicking on b, e and h, the trial will continue (maybe showing a text saying ‘done’ and a button to continue). I do not need to log any data here, as this is just an intermediate step to make sure that the subject does some recalling exercises.

    I think the example is pretty straight forward, so I hope you can point me in the right direction.

    Thanks in advance

    #6705
    Jeremy
    Keymaster

    Hi,

    Here’s a live demo: https://farm.pcibex.net/r/EMYHvp/

    And here’s the code (you can edit it directly on the farm too):

    newTargetButton = (name,text) => newButton(name, (name||text))
        .callback(
            getButton(name)
                .disable()
                .css("color","red")
            ,
            getVar("targetsLeft")
                .set(v=>v-1)
                .test.is(0)
                .success( getButton("Next").click() )
            ,
            getText("counter")
                .text( getVar("targetsLeft") )
        )
        .selector("buttons")
        
    newFilerButton = (name,text) => newButton(name, (name||text))
        .callback( getButton(name).disable() )
        .selector("buttons")
    
    newTrial(
        newVar("targetsLeft", 3)
        ,
        newText("counter", '3')
            .before( newText("# targets left: ") )
            .print()
        ,
        newCanvas("container", 800,100)
        ,
        newSelector("buttons").disableClicks()
        ,
        newTargetButton("target1").print(   0,0,getCanvas("container")),
        newTargetButton("target2").print(200, 0,getCanvas("container")),
        newTargetButton("target3").print(400, 0,getCanvas("container"))
        ,
        newFilerButton("filler1").print(600, 0,getCanvas("container")),
        newFilerButton("filler2").print(  0,40,getCanvas("container")),
        newFilerButton("filler3").print(200,40,getCanvas("container")),
        newFilerButton("filler4").print(400,40,getCanvas("container")),
        newFilerButton("filler5").print(600,40,getCanvas("container")),
        newFilerButton("filler6").print(  0,80,getCanvas("container")),
        newFilerButton("filler7").print(200,80,getCanvas("container"))
        ,
        getCanvas("container").print()
        ,
        getSelector("buttons").shuffle()
        ,
        newButton("Next").wait()
    )

    Let me know if you have any questions

    Jeremy

    #6707
    candido.otero
    Participant

    Thank you for the ultra-fast reply. That’s exactly the behaviour I was looking for.
    It’s also a very nice example of the use of the Selector element along with the Canvas. Much appreciated.

Viewing 3 posts - 1 through 3 (of 3 total)
  • You must be logged in to reply to this topic.