Skip to main content
Version: latest

iframe

「代码配置」用于给 HTML 题挂载可交互的外部页面。目前支持的类型为 iframe:把一个外部页面以全屏弹层的形式嵌入答题流程,并在交互结束后把数据回传给问卷。

配置 iframe

在问卷编辑页右侧边栏的「代码配置」区域:

  1. 类型选择 iframe,点击右侧蓝色「+」新增一个 iframe 配置(可添加多个,点击 tab 上的红色减号可删除)。
  2. 填写以下字段:
    • URL(必填):要嵌入的页面地址,需为合法的 http/https 链接。
    • 触发 id(必填):与 HTML 内容里某个元素的 id 对应。

触发方式

答题时,问卷会用「触发 id」去匹配 HTML 内容中相同 id 的元素。当受访者点击该元素时,会弹出一个全屏遮罩,中间展示配置的 iframe 页面(右上角有关闭按钮)。

嵌入的页面运行在受限沙箱里,只保留脚本执行与 postMessage 通信能力,本地存储、页面跳转、弹窗等能力都不可用。

例如 HTML 内容里有:

<button id="iframe-c1Vp">开始游戏</button>

只要把某个 iframe 的「触发 id」设为 iframe-c1Vp,点击该按钮就会打开对应的 iframe。

与外部页面通信

iframe 页面通过浏览器标准的 postMessage 与问卷页(父页面)通信,用来回传作答数据、通知作答完毕。回传的数据会依据你定义的自定义变量进行收集。

父页面 → iframe:初始化数据下发

每次 iframe 加载完成时,问卷页会主动向 iframe 下发一次「已保存的作答数据」(即上一轮作答结果),方便页面恢复/回填之前的作答状态:

window.addEventListener('message', (event) => {
const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
if (data && data.__WJ_IFRAME_INIT_DATA__) {
const answers = data.__WJ_IFRAME_INIT_DATA__; // 二维数组 [[{ key, time, value }], ...]
// 用 answers 恢复页面状态...
}
});

__WJ_IFRAME_INIT_DATA__ 的值即该题已保存的答案(二维批次数组,结构见自定义变量 · 答案数据结构);首次作答时为空数组 []。本轮新收集的数据不在其中,且会在作答完毕后覆盖它。

由于沙箱内的页面无法使用本地存储,跨轮次需要保留的状态请一律通过这份初始化数据取回。收到该消息时 event.origin 为问卷页的 origin,如需校验可据此判断。

iframe → 父页面:回传数据

iframe 内使用 postMessage 向父页面发送消息:

window.parent.postMessage(payload, targetOrigin);
  • payload必须是一个 JSON 对象(也可传该对象 JSON.stringify 后的字符串,父页面会自动解析)。
  • targetOrigin:填 '*' 即可。消息只投递给 window.parent 这一个窗口,不会外泄;如需严格指定,填问卷页的 origin。注意沙箱页面读不到 document.referrer,靠它推断问卷页 origin 会失败。

必须在 iframe 页面自身的顶层脚本里调用 window.parent.postMessage。从新开的窗口、或页面内再嵌套一层子 iframe 发送的消息,问卷都不会采纳。

消息对象的 key 只能是两类:

key含义
<自定义变量名>自定义变量里定义的变量名,用于回传数据
__WJ_IFRAME_QUESTION_END__固定结束关键词,表示本题作答完毕;同时也会作为一条数据写入所在批次

上报变量(可分多次、可一次多个)。同一轮作答内,每次 postMessage 会作为一批独立存储、互不覆盖

window.parent.postMessage({ score: 90 }, '*');
window.parent.postMessage({ score: 90, level: 'A' }, '*');

上例最终存为二维数组(结构见自定义变量 · 答案数据结构)。

交互结束时发送结束关键词,问卷会关闭弹层并把收集到的数据作为答案:

window.parent.postMessage({ __WJ_IFRAME_QUESTION_END__: 1 }, '*');
// 也可与最后一批变量一起发送
window.parent.postMessage({ score: 90, __WJ_IFRAME_QUESTION_END__: 1 }, '*');

父页面的处理规则

  • 来源校验:问卷页只接受当前这个 iframe 窗口发来的消息(以窗口引用为凭据),其它来源的消息一律忽略。
  • 类型校验:payload 必须能解析为纯对象,否则丢弃。
  • 体积限制:单条消息序列化后超过 200000 字符会被整条丢弃;一轮作答内最多收集 2000 批数据,超出的消息会被忽略。postMessage 不适合搬运大体积素材,请不要把 base64 图片、逐帧采样等原始数据直接回传。
  • 按批收集:一次消息里命中自定义变量的 key 与 __WJ_IFRAME_QUESTION_END__ 组成「一批」(共用同一时间戳)整批入库,同一轮内互不覆盖;__WJ_IFRAME_QUESTION_END__ 同时触发结束;其余未知 key 会被静默忽略,但不会导致整条消息作废。
  • 按轮覆盖:只有收到 __WJ_IFRAME_QUESTION_END__ 才会把本轮数据写入答案;重新打开 iframe 会开启新一轮,见下方重复作答

重复作答

一次「打开 iframe 弹层 → 发送结束关键词」为一轮作答

  • 受访者再次点击触发元素时,本题会重新开始收集,作答完毕后用本轮数据整体覆盖上一轮结果,不会与旧数据叠加。
  • 中途关闭弹层(未发送结束关键词)时,本轮已回传的数据会被丢弃,答案保持为上一轮的结果。
  • 同一道题配置了多个 iframe 时,覆盖只作用于重新打开的那个 iframe,同一次答题过程中其它 iframe 已收集的数据仍会保留。

完整示例

假设已配置两个自定义变量 scorelevel,iframe 页面内:

<script>
const report = (vars) => window.parent.postMessage(vars, '*');

report({ score: 90 });
report({ level: 'A' });
report({ __WJ_IFRAME_QUESTION_END__: 'O9yLpWmmHv' }); // 结束,自己定义回传用户标识
</script>

必答判定

  • 含 iframe 的 HTML 题:只有在收到 __WJ_IFRAME_QUESTION_END__ 后才算「已作答」;若为必答题,未收到结束关键词时翻页/提交会被拦截。
  • 不含 iframe 的 HTML 题:默认视为已作答(纯展示)。

因此,iframe 交互结束时务必发送一次结束关键词。

相关