本页面的其他翻译:

输入型填空题模板

本模板主要用于构建填空题。用户提供需要背诵的文段,Anki将根据挖空率随机挖空,最终,用户通过填空来达到背诵的目的。具体效果如下:

功能介绍

为了适应不同的应用场景,模板设计了几种语法。

跳过挖空

使用(())包裹的内容不参与挖空

指定挖空

使用[[]]语法表示该文段必须被挖空,并且在挖去这些文段后,才计算剩余的挖空率

指定随机挖空

有时候我们想背诵的诗句往往有强关联,比如满堂花醉三千客,一剑霜寒十四州,我们想背诵第一句,对第二句挖空,或者想背诵第二句而对第一句挖空,这时候可以使用[[满堂花醉三千客,|一剑霜寒十四州]],这个语法将对第一句或者第二句随机挖空

错误处理

为了方便用户和开发者交流,当用户把挖空率设置为999时,anki将输出挖空前的原始文本和挖空后的文本。你可以把这些信息粘贴在下面的评论区,开发者根据这些信息进行错误处理

源码

下面提供模板的源码。

正面

点击查看正面内容模板

点击查看正面内容模板

  1. <div class="title">{{text:标题}}</div>
  2. <div class="author">{{text:作者}}</div>
  3. <div class="test">
  4. <div class="sentence" id="sentence-area" style="line-height: 1.8; text-align: justify; white-space: pre-wrap;">{{text:文段}}</div>
  5. </div>
  6. <div id="lang-type" style="display:none">{{语言}}</div>
  7.  
  8. <div id="debug-panel" style="display: none; text-align: left; font-family: 'Consolas', monospace; font-size: 11px; background: #1a1a1a; color: #ddd; padding: 15px; margin-top: 20px; border-radius: 8px; border: 2px solid #444; overflow-x: auto;">
  9. <h3 style="color: #0f0; margin: 0 0 10px 0; border-bottom: 1px solid #444; padding-bottom: 5px;">🔬 专家诊断系统 (Rate=999)</h3>
  10.  
  11. <div style="margin-bottom: 15px;">
  12. <b style="color: #ff0;">【1】原始HTML (Raw Data):</b>
  13. <pre id="debug-raw-html" style="background: #222; padding: 10px; border-radius: 4px; color: #ccc; white-space: pre-wrap; word-break: break-all; margin: 5px 0;"></pre>
  14. </div>
  15.  
  16. <div style="margin-bottom: 15px;">
  17. <b style="color: #ff0;">【2】解析分词 (Tokenization Stream):</b>
  18. <div id="debug-tokens" style="background: #222; padding: 10px; border-radius: 4px; line-height: 2.2; margin: 5px 0;"></div>
  19. </div>
  20.  
  21. <div id="debug-stats" style="color: #0ff; font-weight: bold; font-size: 13px;"></div>
  22. </div>
  23.  
  24. <script type="text/javascript">
  25. (function() {
  26. var sentenceArea = document.getElementById("sentence-area");
  27. if (!sentenceArea) return;
  28.  
  29. var rawRateStr = "{{text:挖空率}}".trim();
  30. var rateNum = parseInt(rawRateStr.replace(/[^0-9]/g, ""));
  31. var isDebugMode = (rateNum === 999);
  32. var rate = isDebugMode ? 70 : (isNaN(rateNum) ? 70 : rateNum);
  33.  
  34. var rawContent = sentenceArea.innerText || sentenceArea.textContent;
  35.  
  36. // 1. 强化正则:非贪婪匹配 [[...]] 和 ((...)),优先捕获
  37. var complexRegex = /\(\([\s\S]*?\)\)|\[\[[\s\S]*?\]\]|\n|\r|[^\S\n\r]+|[a-zA-Z0-9-']+|[\u4e00-\u9fa5]|./g;
  38.  
  39. var allUnits = [];
  40. var mandatoryIndices = [];
  41. var optionalIndices = [];
  42. var match;
  43.  
  44. // 标点符号黑名单:确保这些字符绝不挖空
  45. var puncBlacklist = /[,。!?;:、“”()《》…—·\[\]\(\)\{\}\s\t\r\n]/;
  46.  
  47. while ((match = complexRegex.exec(rawContent)) !== null) {
  48. var token = match[0];
  49.  
  50. if (token.startsWith("((")) {
  51. allUnits.push({ text: token.slice(2, -2), canBlank: false, isTarget: false, type: "protected" });
  52. }
  53. else if (token.startsWith("[[")) {
  54. var inner = token.slice(2, -2);
  55. if (inner.indexOf("|") !== -1) {
  56. var parts = inner.split("|");
  57. var randIndex = Math.random() < 0.5 ? 0 : 1;
  58. parts.forEach(function(p, idx) {
  59. var isBlank = (idx === randIndex);
  60. if (isBlank) mandatoryIndices.push(allUnits.length);
  61. allUnits.push({ text: p, canBlank: isBlank, isTarget: isBlank, isLogicGroup: true });
  62. });
  63. } else {
  64. mandatoryIndices.push(allUnits.length);
  65. allUnits.push({ text: inner, canBlank: true, isTarget: false });
  66. }
  67. }
  68. else if (token === "\n" || token === "\r") {
  69. allUnits.push({ text: token, canBlank: false, isTarget: false, isNewline: true });
  70. }
  71. else {
  72. var isWordOrNum = /[a-zA-Z0-9\u4e00-\u9fa5]/.test(token);
  73. var isDe = /^[的是了有]$/.test(token);
  74. // 关键:排除黑名单中的标点符号
  75. var isPunctuation = puncBlacklist.test(token);
  76. var canBlank = isWordOrNum && !isDe && !isPunctuation;
  77.  
  78. if (canBlank) optionalIndices.push(allUnits.length);
  79. allUnits.push({ text: token, canBlank: canBlank, isTarget: false });
  80. }
  81. }
  82.  
  83. var totalPotential = mandatoryIndices.length + optionalIndices.length;
  84. var globalQuota = Math.round(totalPotential * rate / 100);
  85. mandatoryIndices.forEach(function(idx) { allUnits[idx].isTarget = true; });
  86. var neededMore = globalQuota - mandatoryIndices.length;
  87.  
  88. if (neededMore > 0 && optionalIndices.length > 0) {
  89. var shuffled = optionalIndices.sort(function() { return 0.5 - Math.random(); });
  90. shuffled.slice(0, neededMore).forEach(function(idx) { allUnits[idx].isTarget = true; });
  91. }
  92.  
  93. if (isDebugMode) {
  94. var debugPanel = document.getElementById("debug-panel");
  95. if (debugPanel) {
  96. debugPanel.style.display = "block";
  97. document.getElementById("debug-raw-html").innerText = rawContent;
  98. document.getElementById("debug-tokens").innerHTML = allUnits.map(u => {
  99. if (u.isNewline) return "<span style='color:#f0f; border:1px solid #f0f;'>[↵]</span><br>";
  100. let color = u.isTarget ? "#0f0" : (u.canBlank ? "#777" : "#f44");
  101. return `<span style="color:${color}; padding:0 1px;">${u.text.replace(/ /g, "·")}</span>`;
  102. }).join("");
  103. document.getElementById("debug-stats").innerText = `统计:手动/组挖空 ${mandatoryIndices.length} | 随机库 ${optionalIndices.length} | 选中总计 ${allUnits.filter(u=>u.isTarget).length}`;
  104. }
  105. }
  106.  
  107. var finalHtml = "";
  108. for (var k = 0; k < allUnits.length; k++) {
  109. var curr = allUnits[k];
  110. if (curr.isTarget) {
  111. var combinedText = curr.text;
  112. if (!curr.isLogicGroup) {
  113. while (k + 1 < allUnits.length && allUnits[k+1].isTarget && !allUnits[k+1].isNewline && !allUnits[k+1].isLogicGroup) {
  114. combinedText += allUnits[k+1].text;
  115. k++;
  116. }
  117. }
  118. var isCN = /[\u4e00-\u9fa5]/.test(combinedText);
  119. var width = isCN ? (combinedText.length * 28 + 15) : (combinedText.length * 12 + 18);
  120. finalHtml += '<input type="text" class="answer-box" style="width:' + width + 'px" data="' + combinedText + '" autocomplete="off" spellcheck="false"/>';
  121. } else {
  122. finalHtml += curr.isNewline ? "<br>" : curr.text;
  123. }
  124. }
  125. sentenceArea.innerHTML = finalHtml;
  126.  
  127. var ipts = Array.from(sentenceArea.getElementsByTagName("input"));
  128. if (ipts.length > 0) {
  129. ipts[0].focus();
  130. ipts.forEach(function(ab, idx) {
  131. ab.addEventListener('input', function() { check(this, idx); });
  132. ab.addEventListener('keydown', function(e) {
  133. if (e.keyCode === 13 && ipts[idx+1]) ipts[idx+1].focus();
  134. if (e.keyCode === 8 && this.value === "" && idx > 0) {
  135. var prev = idx - 1;
  136. while(prev >= 0 && ipts[prev].readOnly) prev--;
  137. if(prev >= 0) ipts[prev].focus();
  138. }
  139. });
  140. });
  141. function check(el, i) {
  142. var target = el.getAttribute("data").toLowerCase();
  143. var val = el.value.trim().toLowerCase();
  144. if (val === target) {
  145. el.className = "answer-box right-answer";
  146. el.value = el.getAttribute("data");
  147. el.readOnly = true;
  148. el.blur();
  149. if (ipts[i + 1]) ipts[i + 1].focus();
  150. } else if (val.length > 0 && target.indexOf(val) !== 0) {
  151. el.className = "answer-box wrong-answer";
  152. } else {
  153. el.className = "answer-box";
  154. }
  155. }
  156. }
  157. })();

背面

点击查看背面内容模板

点击查看背面内容模板

<div class="test">
	<div class="translation">
		{{text:语言}}
	</div>
	<div class="sentence" id="sentence-area">{{text:文段}}</div>
</div>
{{#笔记}}
<hr>
<div class="note">{{笔记}}</div>
{{/笔记}}
 
<script type="text/javascript">
var sentenceArea = document.getElementById("sentence-area");
    if (sentenceArea) {
        var html = sentenceArea.innerHTML;
 
        // 1. 处理互斥组 [[A|B]] -> 还原为 AB
        html = html.replace(/\[\[(.*?)\|(.*?)\]\]/g, "$1$2");
 
        // 2. 处理强制组 [[A]] -> 还原为 A
        // 注意:这里使用了循环匹配,防止连续的 [[A]][[B]] 导致漏删括号
        while (html.indexOf("[[") !== -1 || html.indexOf("]]") !== -1) {
            html = html.replace(/\[\[(.*?)\]\]/g, "$1");
            // 兜底:清理掉可能残余的孤立括号
            html = html.replace(/\[\[/g, "").replace(/\]\]/g, "");
        }
 
        // 3. 处理保护区 ((A)) -> 还原为 A
        html = html.replace(/\(\((.*?)\)\)/g, "$1");
 
        // 4. 处理普通括号 (A) -> 还原为 A
        html = html.replace(/\(|\)/g, "");
 
        sentenceArea.innerHTML = html;
    }
</script>

样式

点击查看样式

点击查看样式

@import url('https://cdn.jsdelivr.net/npm/lxgw-wenkai-screen-web/style.css?cache=1y');
.card {
    font-family: "LXGW WenKai Screen","PingFang SC", "Microsoft YaHei", arial, sans-serif;
    font-size: 20px;
    color: #333;
    background-color: white;
    padding: 15px;
    line-height: 1.6;
}
 
.sentence {
    font-size: 26px;
    margin: 20px 0;
    line-height: 2.2; /* 增加行高,给长输入框留出空间 */
    text-align: justify;
}
 
/* 核心:输入框样式优化 */
.answer-box {
    border: none;
    border-bottom: 2px solid #000; /* 加粗底线,更有辨识度 */
    background-color: #f8f9fa; /* 淡淡的底色,让长格子一眼可见范围 */
    text-align: center;
    outline: none;
    font-size: 26px;
    font-family: inherit;
    margin: 0 4px;
    padding: 0 5px;
    transition: all 0.2s;
    max-width: 95%; /* 关键:防止超长句子在手机上横向溢出 */
    border-radius: 4px 4px 0 0;
}
 
/* 聚焦时的视觉反馈 */
.answer-box:focus {
    background-color: #e9ecef;
    border-bottom-color: #007bff; /* 聚焦时底线变蓝 */
}
 
.right-answer {
    color: #28a745 !important;
    background-color: #e8f5e9 !important;
    border-bottom-color: #28a745 !important;
}
 
.wrong-answer {
    color: #dc3545 !important;
    background-color: #ffebee !important;
    border-bottom-color: #dc3545 !important;
}
 
.title {
    font-size: 32px;
    font-weight: bold;
    text-align: center;
    margin-bottom: 10px;
}
 
.author {
    font-size: 18px;
    color: #666;
    text-align: center;
    margin-bottom: 20px;
}
 
.translation {
    font-size: 20px;
    margin: 10px 0;
    color: #555;
    border-left: 4px solid #ddd;
    padding-left: 10px;
}

下载

为方便用户,本站提供导出的牌组下载

评论

请输入您的评论. 可以使用维基语法:
请在输入框中填入验证码以证明您不是机器人。 Z᠎ C K M᠎ L