You need to enable JavaScript to run this app. 纯HTML/CSS/原生JS实现单元素放置的拖拽测验及答案校验
优惠活动
大模型
产品
解决方案
定价
更多

纯HTML/CSS/原生JS实现单元素放置的拖拽测验及答案校验

纯原生实现拖拽测验的问题解决方案

可行性结论

两个核心功能完全可以仅靠HTML、CSS、原生JavaScript实现,无需依赖第三方库、PHP等后端能力,浏览器原生提供的HTML5拖拽API可完整覆盖所有交互需求。

现有代码前置问题修正

先修复基础缺陷,否则后续功能无法正常运行:

  • 移除重复元素id:原代码中两个可拖拽元素都使用了id="dragtarget",DOM中元素id必须全局唯一,否则会导致元素选取逻辑错误
  • 补充事件边界判断:原代码通过全局事件监听判断元素类名,当拖拽路径经过放置区内部的子元素时,会导致事件判断失效

核心功能实现逻辑

1. 限制放置区仅接收1个拖拽元素

无需额外定义状态变量,直接利用DOM结构特征判断即可:

  • 触发drop事件时,先检查目标放置区的子元素数量,如果已经大于0,直接拦截后续的元素追加操作,避免多个元素堆叠
  • 优化体验:在dragover阶段判断放置区是否被占用,对已占用的区域设置禁止放置的鼠标光标样式,提前给用户操作反馈
  • 支持拖拽回退:用户可以把放错位置的答案元素直接拖回原选项容器,不会出现元素永久卡在放置区的问题

2. 实现拖拽完成后的自动校验

所有校验逻辑都可在前端完成,不需要后端参与:

  • 给每个空白放置区添加自定义属性data-answer,提前标记该位置对应的正确答案,例如<div class="droptarget" data-answer="A,B,C"></div>
  • 编写独立校验函数,遍历页面上所有的放置区元素:
    • 读取每个放置区绑定的正确答案值
    • 获取放置区内部当前放置的答案元素的文本内容
    • 二者匹配则给放置区标记为正确样式,不匹配则标记为错误样式
  • 可在用户点击提交按钮、拖拽放置完成等时机触发校验函数,实现实时反馈

完整修正可运行代码

样式代码(CSS)

.droptarget {
    display: inline-block;
    min-width: 50px;
    height: 25px;
    border: 1px solid #aaaaaa;
    color: #000;
    text-align: center;
    margin: 0 5px;
    vertical-align: middle;
}
.container {
    display: inline-block;
    padding: 15px;
    margin: 10px;
    background: #eee;
    border: 2px solid black;
    border-radius: 5px;
    box-sizing: border-box;
}
.dragtarget {
    background-color: red;
    padding: 5px;
    border-radius: 5px;
    color: #fff;
    font-weight: bold;
    text-align: center;
    margin: 5px;
}
.domande {
    display: inline-block;
    padding: 15px;
    margin: 10px;
    background: #eee;
    border: 2px solid black;
    border-radius: 5px;
    box-sizing: border-box;
}
/* 新增校验状态样式 */
.droptarget.correct {
    border: 2px solid #2ecc71;
    background: #e8f8f0;
}
.droptarget.wrong {
    border: 2px solid #e74c3c;
    background: #fdecea;
}

结构代码(HTML)

<p>拖拽答案到对应题目后的空白位置</p>

<!-- 可拖拽选项容器 -->
<div class="container" id="answerPool">
    <p draggable="true" class="dragtarget" id="opt1">A,B,C</p>
    <p draggable="true" class="dragtarget" id="opt2">1,2,3</p>
</div>

<!-- 题目容器 -->
<div class="domande">
    <h3>字母表前三个字母是:<div class="droptarget" data-answer="A,B,C"></div></h3>
    <h3>最开始的三个自然数是:<div class="droptarget" data-answer="1,2,3"></div></h3>
</div>

<button id="checkBtn">提交校验</button>
<p id="demo"></p>

交互逻辑代码(JavaScript)

let draggedElement = null;

/* 拖拽源相关事件 */
document.addEventListener("dragstart", function(event) {
    // 仅响应可拖拽类元素的触发
    if (event.target.classList.contains('dragtarget')) {
        draggedElement = event.target;
        event.dataTransfer.setData("Text", event.target.id);
        document.getElementById("demo").innerHTML = "开始拖拽:" + event.target.textContent;
        event.target.style.opacity = "0.4";
    }
});

document.addEventListener("drag", function(event) {
    document.getElementById("demo").style.color = "red";
});

document.addEventListener("dragend", function(event) {
    if (event.target.classList.contains('dragtarget')) {
        document.getElementById("demo").innerHTML = "拖拽结束";
        event.target.style.opacity = "1";
        draggedElement = null;
    }
});

/* 放置区相关事件 */
document.addEventListener("dragenter", function(event) {
    if (event.target.classList.contains("droptarget")) {
        event.target.style.border = "3px dotted red";
    }
});

document.addEventListener("dragover", function(event) {
    // 阻止浏览器默认行为,允许元素放置
    event.preventDefault();
    // 对已被占用的放置区设置禁止放置状态
    if (event.target.classList.contains("droptarget") && event.target.children.length > 0) {
        event.dataTransfer.dropEffect = "none";
    } else {
        event.dataTransfer.dropEffect = "move";
    }
});

document.addEventListener("dragleave", function(event) {
    if (event.target.classList.contains("droptarget")) {
        event.target.style.border = "";
    }
});

document.addEventListener("drop", function(event) {
    event.preventDefault();
    if (event.target.classList.contains("droptarget")) {
        // 核心限制:放置区已有元素时直接拦截
        if (event.target.children.length > 0) {
            return;
        }
        document.getElementById("demo").style.color = "";
        event.target.style.border = "";
        const draggedId = event.dataTransfer.getData("Text");
        event.target.appendChild(document.getElementById(draggedId));
        // 放置完成后清除之前的校验状态
        event.target.classList.remove('correct', 'wrong');
    }
});

/* 答案校验函数 */
function checkAnswers() {
    const dropTargets = document.querySelectorAll('.droptarget');
    let correctCount = 0;
    dropTargets.forEach(target => {
        const expectedAnswer = target.dataset.answer;
        const placedElement = target.firstElementChild;
        // 清除旧状态
        target.classList.remove('correct', 'wrong');
        // 未放置内容时跳过校验
        if (!placedElement) {
            return;
        }
        // 对比内容是否匹配
        if (placedElement.textContent.trim() === expectedAnswer) {
            target.classList.add('correct');
            correctCount++;
        } else {
            target.classList.add('wrong');
        }
    });
    document.getElementById('demo').textContent = `校验完成:共答对 ${correctCount} / ${dropTargets.length} 题`;
}

// 绑定提交按钮的校验触发事件
document.getElementById('checkBtn').addEventListener('click', checkAnswers);

内容的提问来源于stack exchange,提问作者abh3698

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 04:45:04