Simplify canvas-button-response plugin DOM structure and make button_html a function parameter

This commit is contained in:
bjoluc 2023-09-05 11:01:37 +02:00
parent bcf2642b89
commit 5970531585
2 changed files with 52 additions and 75 deletions

View File

@ -3,7 +3,7 @@
<head>
<script src="../packages/jspsych/dist/index.browser.js"></script>
<script src="../packages/plugin-canvas-button-response/dist/index.browser.js"></script>
<link rel="stylesheet" href="../packages/jspsych/css/jspsych.css">
<link rel="stylesheet" href="../packages/jspsych/css/jspsych.css" />
</head>
<body></body>
<script>
@ -78,7 +78,7 @@
timeline: [{
type: jsPsychCanvasButtonResponse,
stimulus: function (c) {
filledCirc(c, jsPsych.timelineVariable('radius'), jsPsych.timelineVariable('color'));
filledCirc(c, jsPsych.evaluateTimelineVariable('radius'), jsPsych.evaluateTimelineVariable('color'));
},
choices: ['Red', 'Green', 'Blue'],
prompt: '<p>What color is the circle?</p>',
@ -92,6 +92,5 @@
};
jsPsych.run([circle_1, circle_2, lines, circle_procedure]);
</script>
</html>
</html>

View File

@ -16,12 +16,16 @@ const info = <const>{
default: undefined,
array: true,
},
/** The html of the button. Can create own style. */
/**
* A function that, given a choice and its index, returns the HTML string of that choice's
* button.
*/
button_html: {
type: ParameterType.HTML_STRING,
type: ParameterType.FUNCTION,
pretty_name: "Button HTML",
default: '<button class="jspsych-btn">%choice%</button>',
array: true,
default: function (choice: string, choice_index: number) {
return `<button class="jspsych-btn">${choice}</button>`;
},
},
/** Any content here will be displayed under the button. */
prompt: {
@ -85,73 +89,50 @@ class CanvasButtonResponsePlugin implements JsPsychPlugin<Info> {
constructor(private jsPsych: JsPsych) {}
trial(display_element: HTMLElement, trial: TrialType<Info>) {
// create canvas
var html =
'<div id="jspsych-canvas-button-response-stimulus">' +
'<canvas id="jspsych-canvas-stimulus" height="' +
trial.canvas_size[0] +
'" width="' +
trial.canvas_size[1] +
'"></canvas>' +
"</div>";
// Create canvas
const stimulusElement = document.createElement("div");
stimulusElement.id = "jspsych-canvas-button-response-stimulus";
//display buttons
var buttons = [];
if (Array.isArray(trial.button_html)) {
if (trial.button_html.length == trial.choices.length) {
buttons = trial.button_html;
} else {
console.error(
"Error in canvas-button-response plugin. The length of the button_html array does not equal the length of the choices array"
);
}
} else {
for (var i = 0; i < trial.choices.length; i++) {
buttons.push(trial.button_html);
}
}
html += '<div id="jspsych-canvas-button-response-btngroup">';
for (var i = 0; i < trial.choices.length; i++) {
var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
html +=
'<div class="jspsych-canvas-button-response-button" style="display: inline-block; margin:' +
trial.margin_vertical +
" " +
trial.margin_horizontal +
'" id="jspsych-canvas-button-response-button-' +
i +
'" data-choice="' +
i +
'">' +
str +
"</div>";
}
html += "</div>";
const canvasElement = document.createElement("canvas");
canvasElement.id = "jspsych-canvas-stimulus";
canvasElement.height = trial.canvas_size[0];
canvasElement.width = trial.canvas_size[1];
stimulusElement.appendChild(canvasElement);
//show prompt if there is one
display_element.appendChild(stimulusElement);
// Display buttons
const buttonGroupElement = document.createElement("div");
buttonGroupElement.id = "jspsych-canvas-button-response-btngroup";
buttonGroupElement.style.cssText = `
display: flex;
justify-content: center;
gap: ${trial.margin_vertical} ${trial.margin_horizontal};
padding: ${trial.margin_vertical} ${trial.margin_horizontal};
`;
for (const [choiceIndex, choice] of trial.choices.entries()) {
buttonGroupElement.insertAdjacentHTML("beforeend", trial.button_html(choice, choiceIndex));
const buttonElement = buttonGroupElement.lastChild as HTMLElement;
buttonElement.dataset.choice = choiceIndex.toString();
buttonElement.addEventListener("click", () => {
after_response(choiceIndex);
});
}
display_element.appendChild(buttonGroupElement);
// Show prompt if there is one
if (trial.prompt !== null) {
html += trial.prompt;
display_element.insertAdjacentHTML("beforeend", trial.prompt);
}
display_element.innerHTML = html;
//draw
let c = document.getElementById("jspsych-canvas-stimulus");
trial.stimulus(c);
trial.stimulus(canvasElement);
// start time
var start_time = performance.now();
// add event listeners to buttons
for (var i = 0; i < trial.choices.length; i++) {
display_element
.querySelector<HTMLButtonElement>("#jspsych-canvas-button-response-button-" + i)
.addEventListener("click", (e: MouseEvent) => {
var btn_el = e.currentTarget as Element;
var choice = btn_el.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
after_response(choice);
});
}
// store response
var response = {
rt: null,
@ -186,14 +167,11 @@ class CanvasButtonResponsePlugin implements JsPsychPlugin<Info> {
// after a valid response, the stimulus will have the CSS class 'responded'
// which can be used to provide visual feedback that a response was recorded
display_element.querySelector("#jspsych-canvas-button-response-stimulus").className +=
" responded";
stimulusElement.classList.add("responded");
// disable all the buttons after a response
var btns = document.querySelectorAll(".jspsych-canvas-button-response-button button");
for (var i = 0; i < btns.length; i++) {
//btns[i].removeEventListener('click');
btns[i].setAttribute("disabled", "disabled");
for (const button of buttonGroupElement.children) {
button.setAttribute("disabled", "disabled");
}
if (trial.response_ends_trial) {
@ -204,9 +182,7 @@ class CanvasButtonResponsePlugin implements JsPsychPlugin<Info> {
// hide image if timing is set
if (trial.stimulus_duration !== null) {
this.jsPsych.pluginAPI.setTimeout(() => {
display_element.querySelector<HTMLElement>(
"#jspsych-canvas-button-response-stimulus"
).style.visibility = "hidden";
stimulusElement.style.visibility = "hidden";
}, trial.stimulus_duration);
}
@ -262,7 +238,9 @@ class CanvasButtonResponsePlugin implements JsPsychPlugin<Info> {
if (data.rt !== null) {
this.jsPsych.pluginAPI.clickTarget(
display_element.querySelector(`div[data-choice="${data.response}"] button`),
display_element.querySelector(
`#jspsych-canvas-button-response-btngroup [data-choice="${data.response}"]`
),
data.rt
);
}