Text questions on PipAPI

48 views
Skip to first unread message

Moshe Poliak

unread,
Sep 5, 2020, 1:56:49 AM9/5/20
to min...@googlegroups.com
Dear Minno Team,

I'm working with Mahzarin Banaji and Benedek Kurdi on a study on Project Implicit. We have run into a dead end using Minno, and we were hoping that perhaps you could help us with your advice, if that's alright.

We have previously coded a study using the pipAPI, where participants are either (1) simply presented with images or (2) presented with images and are asked to press one of four buttons.

We are interested in changing the functionality in (2), where, instead of pressing a button, participants type in their own text and submit. We know how to code this functionality in the questAPI, but we don't know whether this is possible to implement at all with the pipAPI.

Do you know if it is possible to use a text input in the pipAPI? If not, how would you recommend going about this problem?

Thank you so much for your time!
All my very best,
Moshe

Elad Zlotnick

unread,
Sep 9, 2020, 2:14:17 AM9/9/20
to Minno.js
Hi Moshe,

There is no built-in way to do what you are requesting, but it is more than feasible using a custom input.
How do you imagine this functionality?
Do you want the text input to be visible, so that participants can see what they are writing?
Do you want to measure input latency? Of what? (each letter? final letter? only submission?).
I assume submission will be by clicking Enter?

Also, I tend to believe that response times will be significantly longer and more variable for this type of task.
If you do not care so much about ms timing, it might be reasonable to use minno-quest for this task (you still get a time measure, though it is less accurate).

best,
Elad

Elad Zlotnick

unread,
Sep 10, 2020, 4:55:49 AM9/10/20
to Minno.js
Hi Moshe,

So here is one solution, there are other simpler ones, but this one is immersive and rather flexible.

What we do is create a custom input and hide it.
We then force all keyboard input into it by keeping it always in focus.
We pick an existing stimulus and update it with the content of the input every time that it changes, the stimulus is chosen according to it's handle as set into "stimHandle' in the custom event object.
Finally we trigger an even each time a change is made to the input.

This event can be caught and responded to from you interactions.

Following is the custom event:

                {
                    handle: 'input',
                    stimHandle: 'word-stim',
                    on: function(inputObj, canvas, stream){
                        var $listener = stream();

                        // create hidden element
                        var element = document.createElement('input');
                        var stim = canvas.querySelector('[data-handle=' + inputObj.stimHandle + ']');
                        element.style.opacity = 0;
                        element.value = stim.innerHTML;
                        canvas.appendChild(element);

                        // make sure element is always in focus.
                        element.addEventListener('blur', focus);
                        focus();

                        element.addEventListener('input', input);
                        
                        $listener.end.map(function(){
                            element.removeEventListener('blur',focus);
                            element.removeEventListener('input',input);
                            element.remove();
                        });

                        return $listener;

                        function focus(){ element.focus(); }
                        function input(e){ 
                            stim.innerHTML = e.target.value;
                            $listener(e);
                        }
                    }
                }

And this is how you can respond to it within your interactions (in this case, save the value of the input into trial.data.value):
               {
                    conditions: [{type:'inputEquals', value:'input'}],
                    actions: [
                        // set the results of the input into trial.data
                        function(action,event,trial){trial.data.value = event.event.target.value;}
                    ]
                }

Finally, you can use a regular input and interaction to submit/end the trail.

Good luck,
Elad

Moshe Poliak

unread,
Sep 21, 2020, 8:11:57 PM9/21/20
to Minno.js
Hi Elad,

Thank you so much for your help! I would have never been able to do anything close to what you helped me do.

May I ask you for help with finishing this work? I'm stuck at incorporating the code you gave me into my template.

Here's the link to my code, since it's too length to paste here: https://app-prod-03.implicit.harvard.edu/implicit/user/mpoliak/testing9//learn.js

We are interested in the part that start at "Generate TEST trials"
We are looping twice, once over every block and once over every trial. This ends up creating trials whose name is of the form T+BLOCK_TRIAL (e.g. T02 is the third trial of the first block).

What we want to be happening: ITI screen (simple says the word "CHOOSE" on a white screen, defined is 'cue2' in the beginning of the code), after which a shape appears with a place for text input, the first letter of which is already provided. The user types in the rest of the word and clicks enter to submit. If their answer is correct, end the trial and go to the next one. If their answer is wrong, display a red X (already exists as a stimulus called 'error') alongside the right answer, and end the trial after 2 seconds.

What is happening right now: every letter provided by the user submits the trial, so they always get the red X. Also, the ITI interval also causes a red X to appear. Another problem that I couldn't solve is that the user's input stays on autofocus forever on the very first input box, and I couldn't make it switch to the new one.

Any help would be deeply appreciated! Thank you so so much for your time!
All my best,
Moshe

Elad Zlotnick

unread,
Sep 24, 2020, 12:13:46 AM9/24/20
to Minno.js
Hi Moshe,

First, you are missing the interaction that records the user responses:
               {
                    conditions: [{type:'inputEquals', value:'input'}],
                    actions: [
                        // set the results of the input into trial.data
                        function(action,event,trial){trial.data.response = event.event.target.value;}
                    ]
                }
You are defining your correct and incorrect responses incorrectly.

Currently you are defining the incorrect condition as follows:
conditions: [
     {type:'inputEqualsTrial', property:ALL_TEST[i][j].trait, negate:true}
]  

This means that any event that is not named the same as the property ALL_TEST[i][j].trait of trial.data is incorrect.
What you probably want to do is have two conditions.
One that requires this to be a submit event (so checking for correctness happens only when the user presses enter).
The second inquires whether the response entered is indeed correct.
It would look something like the following (of course, for the correct interaction, you should remove the negeat):

conditions: [
     {type:'inputEquals', value:'submit'},
     {type:'trialEquals', property:'response', value:ALL_TEST[i][j].trait, negate:true}
]  

best,
Elad

Moshe Poliak

unread,
Sep 27, 2020, 4:10:09 PM9/27/20
to Minno.js
Dear Elad,

Thank you so so much! It works now, and I just couldn't have done it without you. Now I also better understand the words of Minno in general. Thank you, thank you, thank you!

All best wishes,
Moshe

Elad Zlotnick

unread,
Sep 29, 2020, 9:25:28 AM9/29/20
to Minno.js
My pleasure.
Good luck!

Reply all
Reply to author
Forward
0 new messages