Multiple slider plugin #1147
-
Hi, I need to display multiple sliders on the same page, and randomize their order. In my mind, the idea would be to mix the jspsych-html-slider-response plugin with the jspsych-survey-likert plugin that allows multiple questions on the same page with random order if needed. I tried to mix these two plugins but I cannot make it work. I have seen that another similar issue has been answered (#799) but the code is even harder than the two plugins mentioned before. Do you have an idea how to make it work? |
Beta Was this translation helpful? Give feedback.
Replies: 13 comments 9 replies
-
Hi @YoannJulliard, I like your idea of combining the html-slider-response and survey-likert plugins. If I were you, I'd modify the survey-likert plugin so that each question prompt is displayed with a slider instead of a group of radio buttons. You can do this by modifying the part of the survey-likert plugin that sets up the HTML for the radio button group for each question (starting here), and replace that with code to set up a slider instead, using the html-slider-response code here as a reference. And if you're using the survey-likert plugin as a starting point, then you'll also need to modify the code that runs when the next button is pressed, starting around here, because the survey-likert code is looking for the radio button groups and recording which radio button is selected, but you won't have any radio buttons. Instead you want to find all of the slider (input) elements on the page (using something like the I hope that helps. Feel free to post again here if you need further guidance. |
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert, thank you for your precious help. Nevertheless, I am stuck again on three points:
I tried multiple things to solve my issues but I cannot make it work. Do you have any other advice? I paste below the current version of the "multiple-slider" plugin, which is a clone of the survey-likest plugin with modifications signaled by comments like "Added by Yoann" or "Commented by Yoann".
|
Beta Was this translation helpful? Give feedback.
-
Hi @YoannJulliard, thanks for sharing your code! When you set up the slider element (input type="range"), you want to also store the question name in the 'data-name' attribute. And because the 'name' parameter is optional when you set up the questions, you should also add your own 'name' attribute to the slider, which is just 'Q0', 'Q1', etc., so that you can still identify the questions if a 'name' parameter isn't specified. And each element on the page should have a unique ID, so you can do that by adding the question index html += '<input type="range" value="'+50+'" min="'+0+'" max="'+100+'" step="'+1+'" style="width: 100%;" id="jspsych-html-slider-response-response-'+i+'" name="Q'+i+'" data-name="'+question.name+'"></input>'; To get the slider responses to save, you want to find all the sliders on the page. In the survey-likert plugin, this happens in the var matches = display_element.querySelectorAll('input[type="range"]'); Once you have all of the sliders on the page stored in the for(var index = 0; index < matches.length; index++){
var id = matches[index].name;
var response = matches[index].valueAsNumber;
var obje = {};
if(matches[index].attributes['data-name'].value !== ''){
var name = matches[index].attributes['data-name'].value;
} else {
var name = id;
}
obje[name] = response;
Object.assign(question_data, obje);
} Finally, the reason that the 'required' parameter doesn't work as you expect is explained in this comment. You could implement a similar solution to the one that's described there, except you could do all of it in the plugin file, instead of using the trial's on_load function. |
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert, thank you again for you help, my problem is solved. Everything works for my particular needs, there are four sliders on the same page, the data is stored and identified, the continue button works as expected, and labels for each question are displayed. I share the code below. Nevertheless, I think it would be interesting if the multiple sliders plugin is more general, that it can be applied to as many sliders as one want. The issue to be solved to achieve that concerns the "continue button issue". Indeed, the issue is solved for my special case of 4 sliders, but the plugin would not work for another number of sliders (e.g., 3, 5, or 6 sliders) without editing the plugin code. Thus, I ask once again for your help, do you have an idea on how we could generalize the continue button solution? Once this issue is solved, do you think I could suggest to add the "multiple-slider" plugin in the JsPsych library if I clean up the code and make some options modifiable (e.g., width of the slider, step of the slider, etc.)? If you think it would be possible, would you agree if I add your name in the contributors? Current version of the code (not cleaned):
|
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert and thank you once again, I think we have a great version of the multiple-slider plugin now (even though not perfect yet). I will consider asking @jodeleeuw what he thinks about the plugin, and whether it could be interesting to add it into the JsPsych library (or to replace the jspsych-html-slider plugin by this one). Before to have a better version of the plugin that could be added into the JsPsych library, you can find below the current version of the plugin. I cleaned the code and made some functions customizable. I also added an example code that uses the plugin to display 5 sliders (the example is inspired by the example of the jspsych-survey-likert plugin). I hope this plugin will help some people. Do not hesitate to contact me if you do use it and encounter some troubles. I want to end this thread with a final thank you to @becky-gilbert who did most of the work. Example of multiple-slider plugin use: // define the labels
var scale_1 = [
"Strongly Disagree",
"Neutral",
"Strongly Agree"
];
// define the sliders parameters
var start = 50;
var min = 0;
var max = 100;
var step = 1;
// create the multiple-slider page
var slider_page = {
type: 'multiple-slider',
questions: [
{prompt: "I like vegetables.", name: 'Vegetables', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like fruit.", name: 'Fruit', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like meat.", name: 'Meat', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like dairy.", name: 'Dairy', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like legumes.", name: 'Legumes', labels: scale_1,
slider_start: start, min: min, max: max, step: step}
],
randomize_question_order: true,
require_movement: true,
slider_width: 600
}; Code of the multiple-slider plugin: /**
* multiple-slider
* a jspsych-like plugin for measuring items on a visual analog scale
*
* Yoann Julliard
* Becky Gilbert
* Inspired by Josh de Leeuw's jspsych-multiple-slider and jspsych-html-slider plugins
*
* documentation: docs.jspsych.org
*
*/
jsPsych.plugins['multiple-slider'] = (function() {
var plugin = {};
plugin.info = {
name: 'multiple-slider',
description: '',
parameters: {
questions: {
type: jsPsych.plugins.parameterType.COMPLEX,
array: true,
pretty_name: 'Questions',
nested: {
prompt: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Prompt',
default: undefined,
description: 'Questions that are associated with the slider.'
},
labels: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name:'Labels',
default: [],
array: true,
description: 'Labels of the sliders.',
},
name: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Question Name',
default: '',
description: 'Controls the name of data values associated with this question'
},
min: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Min slider',
default: 0,
description: 'Sets the minimum value of the slider.'
},
max: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Max slider',
default: 100,
description: 'Sets the maximum value of the slider',
},
slider_start: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Slider starting value',
default: 50,
description: 'Sets the starting value of the slider',
},
step: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Step',
default: 1,
description: 'Sets the step of the slider'
}
}
},
randomize_question_order: {
type: jsPsych.plugins.parameterType.BOOL,
pretty_name: 'Randomize Question Order',
default: false,
description: 'If true, the order of the questions will be randomized'
},
preamble: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Preamble',
default: null,
description: 'String to display at top of the page.'
},
button_label: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Button label',
default: 'Continue',
description: 'Label of the button.'
},
autocomplete: {
type: jsPsych.plugins.parameterType.BOOL,
pretty_name: 'Allow autocomplete',
default: false,
description: "Setting this to true will enable browser auto-complete or auto-fill for the form."
},
require_movement: {
type: jsPsych.plugins.parameterType.BOOL,
pretty_name: 'Require movement',
default: false,
description: 'If true, the participant will have to move the slider before continuing.'
},
slider_width: {
type: jsPsych.plugins.parameterType.INT,
pretty_name:'Slider width',
default: 500,
description: 'Width of the slider in pixels.'
}
}
}
plugin.trial = function(display_element, trial) {
var w = '100%';
var html = "";
// inject CSS for trial
html += '<style id="jspsych-multiple-slider-css">';
html += ".jspsych-multiple-slider-statement { display:block; font-size: 16px; padding-top: 40px; margin-bottom:10px; }"+
".jspsych-multiple-slider-opts { list-style:none; width:"+w+"; margin:auto; padding:0 0 35px; display:block; font-size: 14px; line-height:1.1em; }"+
".jspsych-multiple-slider-opt-label { line-height: 1.1em; color: #444; }"+
".jspsych-multiple-slider-opts:before { content: ''; position:relative; top:11px; /*left:9.5%;*/ display:block; background-color:#efefef; height:4px; width:100%; }"+
".jspsych-multiple-slider-opts:last-of-type { border-bottom: 0; }"+
".jspsych-multiple-slider-opts li { display:inline-block; /*width:19%;*/ text-align:center; vertical-align: top; }"+
".jspsych-multiple-slider-opts li input[type=radio] { display:block; position:relative; top:0; left:50%; margin-left:-6px; }"
html += '</style>';
// show preamble text
if(trial.preamble !== null){
html += '<div id="jspsych-multiple-slider-preamble" class="jspsych-multiple-slider-preamble">'+trial.preamble+'</div>';
}
if ( trial.autocomplete ) {
html += '<form id="jspsych-multiple-slider-form">';
} else {
html += '<form id="jspsych-multiple-slider-form" autocomplete="off">';
}
// add sliders questions ///
// generate question order. this is randomized here as opposed to randomizing the order of trial.questions
// so that the data are always associated with the same question regardless of order
var question_order = [];
for(var i=0; i<trial.questions.length; i++){
question_order.push(i);
}
if(trial.randomize_question_order){
question_order = jsPsych.randomization.shuffle(question_order);
}
for (var i = 0; i < trial.questions.length; i++) {
var question = trial.questions[question_order[i]];
// add question
html += '<label class="jspsych-multiple-slider-statement">' + question.prompt + '</label>';
// add labels
html += '<div id="jspsych-html-slider-response-wrapper" style="margin: 0px 0px;">';
html += '<div class="jspsych-html-slider-response-container" style="position:relative; margin: 0 auto 3em auto; ';
html += 'width:'+trial.slider_width+'px;';
html += '">';
html += '<div>';
for(var j=0; j < question.labels.length; j++){
var width = 100/(question.labels.length-1);
var left_offset = (j * (100 /(question.labels.length - 1))) - (width/2);
html += '<div style="display: inline-block; position: absolute; left:'+left_offset+'%; text-align: center; width: '+width+'%;">';
html += '<span style="text-align: center; font-size: 80%; color: grey">'+question.labels[j]+'</span>';
html += '</div>'
}
html += '</div>';
// add some space between sliders and the labels
html += '<br/>';
// add sliders
html += '<input type="range" value="'+question.slider_start+'" min="'+question.min+'" max="'+question.max+'" step="'+question.step+'" style="width: 100%;" class="jspsych-html-slider-response-response" id="jspsych-html-slider-response-response-'+i+'" name="Q'+i+'" data-name="'+question.name+'"></input>';
// add some space between the sliders
html += '<br/>';
}
// add some space before the next button
html += '<br/>'
// add submit button
html += '<input type="submit" id="jspsych-multiple-slider-next" class="jspsych-multiple-slider jspsych-btn" value="'+trial.button_label+'"></input>';
html += '</form>'
display_element.innerHTML = html;
// require responses
if (trial.require_movement) {
// disable by default the next button
document.getElementById('jspsych-multiple-slider-next').disabled = true;
// check whether all sliders have been clicked
function check_reponses() {
var all_sliders = document.querySelectorAll('.jspsych-html-slider-response-response');
var all_clicked = true;
for (var i=0; i<all_sliders.length; i++) {
if (!all_sliders[i].classList.contains("clicked")) {
// if any one slider doesn't have the 'clicked' class, then we know that they haven't all been clicked
all_clicked = false;
break;
}
}
if (all_clicked) {
// if they have been clicked then enable the next button
document.getElementById('jspsych-multiple-slider-next').disabled = false;
}
}
var all_sliders = document.querySelectorAll('.jspsych-html-slider-response-response');
all_sliders.forEach(function(slider) {
slider.addEventListener('click', function() {
slider.classList.add("clicked"); // record the fact that this slider has been clicked
check_reponses(); // each time a slider is clicked, check to see if they've all been clicked
});
});
}
display_element.querySelector('#jspsych-multiple-slider-form').addEventListener('submit', function(e){
e.preventDefault();
// measure response time
var endTime = performance.now();
var response_time = endTime - startTime;
// create object to hold responses
var question_data = {};
// hold responses
var matches = display_element.querySelectorAll('input[type="range"]');
// store responses
for(var index = 0; index < matches.length; index++){
var id = matches[index].name;
var response = matches[index].valueAsNumber;
var obje = {};
if(matches[index].attributes['data-name'].value !== ''){
var name = matches[index].attributes['data-name'].value;
} else {
var name = id;
}
obje[name] = response;
Object.assign(question_data, obje);
}
// save data
var trial_data = {
"rt": response_time,
"responses": JSON.stringify(question_data),
"question_order": JSON.stringify(question_order)
};
display_element.innerHTML = '';
// next trial
jsPsych.finishTrial(trial_data);
});
var startTime = performance.now();
};
return plugin;
})(); |
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert, I did not abandon this discussion, I have been working on the documentation of the plugin, but I could not find the time to work on a testing file. I will try to find some time to work on the testing file in the next week, I keep you informed when I have a first version. I agree that it would be great to propose a request with all the required files 😊. I paste below the current version of the multiple-slider plugin documentation, if you have ideas to improve it do not hesitate: jspsych-multiple-slider pluginThe multiple-slider plugin displays a set of questions with visual analog scale responses. The subject responds by clicking or moving a slider. ParametersParameters with a default value of undefined must be specified. Other parameters can be left unspecified if the default value is acceptable.
Data GeneratedIn addition to the default data collected by all plugins, this plugin collects the following data for each trial.
ExamplesBasic example with two labelsvar scale_1 = [
"Strongly Disagree",
"Strongly Agree"
];
var slider_page = {
type: 'multiple-slider',
questions: [
{prompt: "I like vegetables.", labels: scale_1}
]
}; Advanced example with multiple questions in a random order// define the labels
var scale_1 = [
"Strongly Disagree",
"Neutral",
"Strongly Agree"
];
// define the sliders parameters
var start = 50;
var min = 0;
var max = 100;
var step = 1;
// create the multiple-slider page
var slider_page = {
type: 'multiple-slider',
questions: [
{prompt: "I like vegetables.", name: 'Vegetables', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like fruit.", name: 'Fruit', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like meat.", name: 'Meat', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like dairy.", name: 'Dairy', labels: scale_1,
slider_start: start, min: min, max: max, step: step},
{prompt: "I like legumes.", name: 'Legumes', labels: scale_1,
slider_start: start, min: min, max: max, step: step}
],
randomize_question_order: true,
require_movement: true,
slider_width: 600
}; |
Beta Was this translation helpful? Give feedback.
-
@YoannJulliard the documentation looks great to me! I can make some minor tweaks to the wording after your pull request is merged. And I can help out with the test file if you get stuck with that. Thanks for putting this together! |
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert, I am a bit lost concerning the testing file, I followed the instructions mentioned on jsPsych website (contributing to jsPsych), but I am stuck at writing anything in the testing file. I tried to begin from the jspsych-survey-likert test file but I have no idea how to adapt to the sliders the part where the testing file is supposed to select a response. I tried to look at the jspsych-html-slider testing file but I could not find such a function, it appeared that this testing file tested other aspects of the plugin. I do not know whether it will be of any help but I paste below the jspsych-survey-likert testing file, with a comment above the part I guess I am supposed to edit. However, I wrote an example html file for the multiple-slider plugin, as suggested on jsPsych website, I will be able to put this html example file in the pull request in order to make the pull request as complete as I can. Below is the test file, with a comment where I stuck : const root = '../../';
const utils = require('../testing-utils.js');
jest.useFakeTimers();
describe('multiple-slider plugin', function(){
beforeEach(function(){
require(root + 'jspsych.js');
require(root + 'plugins/jspsych-multiple-slider.js');
});
test('loads correctly', function(){
expect(typeof window.jsPsych.plugins['multiple-slider']).not.toBe('undefined');
});
test('data are logged with the right question when randomize order is true', function(){
var scale = ['a','b','c','d','e'];
var t = {
type: 'multiple-slider',
questions: [
{ prompt: 'Q0', labels: scale },
{ prompt: 'Q1', labels: scale },
{ prompt: 'Q2', labels: scale },
{ prompt: 'Q3', labels: scale },
{ prompt: 'Q4', labels: scale }
],
randomize_question_order: true
}
jsPsych.init({timeline:[t]});
// I have no idea how to adapt this part to the sliders
document.querySelector('input[name="Q0"][value="0"]').checked = true;
document.querySelector('input[name="Q1"][value="1"]').checked = true;
document.querySelector('input[name="Q2"][value="2"]').checked = true;
document.querySelector('input[name="Q3"][value="3"]').checked = true;
document.querySelector('input[name="Q4"][value="4"]').checked = true;
utils.clickTarget(document.querySelector('#jspsych-multiple-slider-next'));
var survey_data = JSON.parse(jsPsych.data.get().values()[0].responses);
expect(survey_data.Q0).toBe(0);
expect(survey_data.Q1).toBe(1);
expect(survey_data.Q2).toBe(2);
expect(survey_data.Q3).toBe(3);
expect(survey_data.Q4).toBe(4);
});
}); |
Beta Was this translation helpful? Give feedback.
-
Hi @becky-gilbert, I hope you are doing well, Thank you for your help and explanations, it worked perfectly. Below I paste the testing file code. I think it is all set now, I can create the pull request and wait to have some feedback about it. Thank you again for your help, I really appreciate that. Here is the code of the testing file: const root = '../../';
const utils = require('../testing-utils.js');
jest.useFakeTimers();
describe('multiple-slider plugin', function(){
beforeEach(function(){
require(root + 'jspsych.js');
require(root + 'plugins/jspsych-multiple-slider.js');
});
test('loads correctly', function(){
expect(typeof window.jsPsych.plugins['multiple-slider']).not.toBe('undefined');
});
test('data are logged with the right question when randomize order is true', function(){
var scale = ['a','b','c','d','e'];
var t = {
type: 'multiple-slider',
questions: [
{ prompt: 'Q0', labels: scale },
{ prompt: 'Q1', labels: scale },
{ prompt: 'Q2', labels: scale },
{ prompt: 'Q3', labels: scale },
{ prompt: 'Q4', labels: scale }
],
randomize_question_order: true
}
jsPsych.init({timeline:[t]});
// simulate a response for each slider
document.querySelector('input[name="Q0"]').value = 0;
document.querySelector('input[name="Q1"]').value = 1;
document.querySelector('input[name="Q2"]').value = 2;
document.querySelector('input[name="Q3"]').value = 3;
document.querySelector('input[name="Q4"]').value = 4;
utils.clickTarget(document.querySelector('#jspsych-multiple-slider-next'));
var survey_data = JSON.parse(jsPsych.data.get().values()[0].responses);
expect(survey_data.Q0).toBe(0);
expect(survey_data.Q1).toBe(1);
expect(survey_data.Q2).toBe(2);
expect(survey_data.Q3).toBe(3);
expect(survey_data.Q4).toBe(4);
});
}); |
Beta Was this translation helpful? Give feedback.
-
Hey @YoannJulliard and @becky-gilbert this seems super useful, thanks for putting it together!! I don't see it on the current plugin list though, is it available? I'd like to use it in a cognition.run paradigm I'm developing. Thanks :) |
Beta Was this translation helpful? Give feedback.
-
Yay!! Thank you so much, Yoann!
I will try to implement this now, appreciate your help and work on this!
…On Sun, Feb 7, 2021 at 12:59 PM YoannJulliard ***@***.***> wrote:
Hi @jackransomlovell <https://github.com/jackransomlovell>, I am very
happy that the plugin will be useful for you :).
For now the plugin is not available in the jsPsych library, it is assigned
for the 7.1 version of jsPsych.
However, you can use the current version of the plugin, as the code is
available in this discussion.
Furthermore, I already tried the plugin within cognition.run and it is
working just well, all you have to do is to upload the plugin within the
cognition.run interface and then you will be able to use it as any jsPsych
plugin.
If you need any help, do not hesitate, I will be happy to help :).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1147 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AL7OWPBKDJEH4SIASHBV3VTS53IIRANCNFSM4TATH3XQ>
.
|
Beta Was this translation helpful? Give feedback.
-
Dear @YoannJulliard, your multiple-slider pugin is great. Unfortunately, I can't use it anymore in JsPsych 7.1. You mentioned above that the plugin would be included in the 7.1 version of JsPsych, but this does not seem to be the case, right? By any chance, did you already make a version of your plugin that is compatible with JsPsych 7.1? Thank a lot for you help. |
Beta Was this translation helpful? Give feedback.
-
I am also interested in multiple sliders. I was just about to copy the plugin code, but I note that this is not working under 7.1. |
Beta Was this translation helpful? Give feedback.
Hi @becky-gilbert and thank you once again, I think we have a great version of the multiple-slider plugin now (even though not perfect yet).
I will consider asking @jodeleeuw what he thinks about the plugin, and whether it could be interesting to add it into the JsPsych library (or to replace the jspsych-html-slider plugin by this one).
Before to have a better version of the plugin that could be added into the JsPsych library, you can find below the current version of the plugin. I cleaned the code and made some functions customizable. I also added an example code that uses the plugin to display 5 sliders (the example is inspired by the example of the jspsych-survey-likert plugin).
I hope…