如何基于参与者响应在jsPsych时间线中动态添加事件?
在jsPsych中根据参与者响应动态插入时间线事件
核心问题分析
你之前的尝试失败原因:
- 直接修改
timeline数组(push/splice)无效:jsPsych调用run()后会创建时间线的内部副本,修改原始数组不会影响正在执行的流程。 jsPsych.addNodeToEndOfTimeline会把Message加到当前时间线末尾,但你提前把Conclusion也加入了时间线,导致Message出现在Conclusion之后,不符合需求。
解决方案一:使用官方条件分支节点(推荐)
jsPsych内置了条件分支支持,通过conditional_function可以控制某个时间线节点是否执行,这是处理这类流程的标准方式,无需手动修改时间线。
完整可运行代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Experiment</title> <script src="https://unpkg.com/jspsych@7.3.3"></script> <script src="https://unpkg.com/@jspsych/plugin-html-keyboard-response@1.1.2"></script> <script src="https://unpkg.com/@jspsych/plugin-html-button-response@1.1.2"></script> <link href="https://unpkg.com/jspsych@7.3.3/css/jspsych.css" rel="stylesheet" type="text/css" /> </head> <body style="background-color: rgb(200, 200, 200);"> <script> // 初始化jsPsych var jsPsych = initJsPsych(); // 随机化选项顺序 var choice = jsPsych.randomization.sampleWithoutReplacement(['A', 'B', 'C'], 3); // 定义各个试次 var Message = { type: jsPsychHtmlKeyboardResponse, stimulus: 'Congrats, It worked! Press Space to Continue', choices: [' '], }; var Conclusion = { type: jsPsychHtmlKeyboardResponse, stimulus: 'The experiment Ended! Press Space to Continue', choices: [' '], }; var ChoiceTrial = { type: jsPsychHtmlButtonResponse, stimulus: 'Choose A to end, or B or C to see a message', choices: choice, }; // 定义条件分支节点:仅当选择非A时执行Message var conditionalMessageNode = { timeline: [Message], conditional_function: function() { // 获取上一个试次的响应结果 var lastResponse = jsPsych.data.get().last(1).values()[0].response; // 返回true则执行当前时间线节点,false则跳过 return lastResponse !== 'A'; } }; // 构建主时间线:ChoiceTrial → 条件Message → Conclusion var timeline = [ChoiceTrial, conditionalMessageNode, Conclusion]; // 运行实验 jsPsych.run(timeline); </script> </body> </html>
关键逻辑说明
conditionalMessageNode作为中间节点,通过conditional_function检查上一个试次的响应:如果选择的不是A,就执行Message试次;如果是A,则直接跳过,进入Conclusion。- 这种方式完全遵循jsPsych的流程设计,代码清晰且不易出错。
解决方案二:动态修改正在运行的时间线(适合复杂场景)
如果需要更灵活的动态插入(比如复杂分支逻辑),可以通过操作jsPsych的内部时间线节点实现,核心是先调整后续节点的顺序,再插入新节点:
关键代码修改
var ChoiceTrial = { type: jsPsychHtmlButtonResponse, stimulus: 'Choose A to end, or B or C to see a message', choices: choice, on_finish: function (data) { if(data.response !== 'A'){ // 获取主时间线节点 var mainTimeline = jsPsych.getTimelineNodeChildren(jsPsych.getMainTimelineNode())[0]; // 移除已经添加的Conclusion节点 mainTimeline.children.pop(); // 插入Message节点 jsPsych.addNodeToEndOfTimeline({timeline: [Message]}, mainTimeline); // 重新添加Conclusion节点 jsPsych.addNodeToEndOfTimeline({timeline: [Conclusion]}, mainTimeline); } } }; // 初始时间线仅包含ChoiceTrial和Conclusion var timeline = [ChoiceTrial, Conclusion]; jsPsych.run(timeline);
逻辑说明
- 在
ChoiceTrial的on_finish回调中,先移除已在时间线末尾的Conclusion,插入Message后再重新添加Conclusion,确保Message出现在Conclusion之前。 - 这种方式适合复杂动态场景,但需要注意时间线节点的层级关系,避免操作错误。
内容的提问来源于stack exchange,提问作者William Mitchell
相关产品推荐
相关产品推荐

