为什么 class 选择器没有在点击操作时创建?

Why class selector is not created on click action?

下面是HTML代码:

<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript">
    var list = document.querySelector('ul');
    list.addEventListener('click', function(ev) {
        if (ev.target.tagName === 'LI') {
            ev.target.classList.toggle('done');
        }
    }, false);
    </script>
</head>

<body>
    <ul>
        <li>Buy milk</li>
        <li>Take the dog for a walk</li>
        <li>Exercise</li>
        <li>Write code</li>
        <li>Play music</li>
        <li>Relax</li>
    </ul>
</body>

</html>

下面是 CSS 代码:

li {
  list-style-type: none;
  position: relative;
  margin: 2px;
  padding: 0.5em 0.5em 0.5em 2em;
  background: lightgrey;
  font-family: sans-serif;
}

li.done {
  background: #CCFF99;
}

li.done::before {
  content: '';
  position: absolute;
  border-color: #009933;
  border-style: solid;
  border-width: 0 0.3em 0.25em 0;
  height: 1em;
  top: 1.3em;
  left: 0.6em;
  margin-top: -1em;
  transform: rotate(45deg);
  width: 0.5em;
}

以上 HTML 代码在浏览器上启动时不会创建 class="done" 点击操作。

fiddle 上,相同的代码工作正常。

由于代码包含在 <head> 中,因此在加载 <body> 的内容之前执行。因此,当绑定事件和未绑定事件时,DOM 中找不到元素。

两种方法可以解决这个问题。

  1. 将代码包装在 DOMContentLoaded 事件回调中。

    document.addEventListener('DOMContentLoaded', function () {
        var list = document.querySelector('ul');
        list.addEventListener('click', function (ev) {
            if (ev.target.tagName === 'LI') {
                ev.target.classList.toggle('done');
            }
        }, false);
    });
    

    已更新 fiddle:https://jsfiddle.net/tusharj/pr2hw6c1/

    阅读有关 DOMContentLoaded 的更多信息:https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

  2. 把代码移到<body>的末尾,这样脚本执行的时候,所有的DOM 个元素已加载。

注意:也可以使用load事件在DOM后执行脚本代码完全 加载,但这将等待页面上的所有资源(图像、框架等)加载,这不需要在元素上绑定事件。供参考阅读 Difference between DOMContentLoaded and Load events


Why does the same code in jsfiddle works?

jsfiddle 提供了四个选项来决定脚本应该被包含在哪里。您可以在库列表下方的左侧面板中设置此选项。

  1. onLoad:这与window的load事件相同。该代码包含在 load 事件的回调中,因此无法从其外部访问 functions/variables。前任。来自 HTML 内联处理程序。
  2. onDomReady:这与jquery的DOMContentLoadedready相同,这里也是代码换行
  3. No wrap - in Head:意思是在head中加入JS代码。 No wrap 表示代码没有像 loadDOMContentLoaded 那样包含在任何函数中。所以定义的functions/variables是全局的。
  4. No wrap - in Body:脚本在<body>元素的末尾加载。

现在我们知道了脚本对于每个选项的行为方式,在 fiddle 中,脚本位置的选项设置为 onLoad,因此代码有效。该代码适用于除 No wrap - in Head.

之外的所有选项

在 jsfiddle 文档上阅读更多相关信息 http://doc.jsfiddle.net/basic/introduction.html#fiddle-settings-sidebar

尝试移动脚本标签并将其放在正文的最后。他们现在的方式是,脚本试图在页面上的元素加载之前 运行。将脚本标签移动到正文底部将确保其上方的所有 html 元素已正确加载。