如何在没有使用jQuery的情况下检测DOM是否已准备好并添加类?

21

我想重写这行代码,不使用jQuery,以便更快地应用它(并且在下载jQuery库之前)。这一行是...

$(document).ready(function() { $('body').addClass('javascript'); });

如果我将代码添加到 html 元素中,是否可以省略 DOM 准备部分?不过这样做的一个问题是验证器不喜欢在 html 元素上使用 class 属性,即使是通过 JS 插入的也是如此。

那么,我该如何在没有 jQuery 的情况下重写代码?


你究竟想实现什么?你想告诉CSS JavaScript已经可用吗?你想尽早地使用特定于浏览器的事件来完成它吗?对于你来说,简单的“onload”事件是否有效?任何额外的输入都可以帮助我们为您提供正确的答案。 - Eugene Lazutkin
2
@Tim Down:它是用JS编写的...并利用DOM API(W3C和Microsoft的API,针对浏览器错误进行了调整),因此它不是“vanilla”(请注意笑脸)。 - Quentin
@David:啊,我想我明白了:你所说的“vanilla JS”是指仅使用语言本身而不涉及任何环境? - Tim Down
通过“vanilla JS”,我认为“这是一个愚蠢的术语”,无法抵制轻微的嘲笑。用“minimal JS”来表达问题可能更好。 - Quentin
12
嘲笑那些知识较少的人并不好笑或酷。我们都曾经处于那个阶段。此外,帮助他理解为什么他的陈述并不语义完美比自以为高人一等更有用。 - Slobaum
显示剩余3条评论
8个回答

48
如果您想重现jQuery的document.ready事件,可以使用适当的onreadystatechangeDOMContentLoaded事件:
function domReady () {
  document.body.className += " javascript";
  // ...
}

// Mozilla, Opera, Webkit 
if ( document.addEventListener ) {
  document.addEventListener( "DOMContentLoaded", function(){
    document.removeEventListener( "DOMContentLoaded", arguments.callee, false);
    domReady();
  }, false );

// If IE event model is used
} else if ( document.attachEvent ) {
  // ensure firing before onload
  document.attachEvent("onreadystatechange", function(){
    if ( document.readyState === "complete" ) {
      document.detachEvent( "onreadystatechange", arguments.callee );
      domReady();
    }
  });
}

参见:https://dev59.com/iHE95IYBdhLWcg3wdtmj - user456584
4
你为什么要分离 readystatechange 事件?IE8 为什么会触发两次这个事件?为什么? - John Dvorak
@Jan 我猜这是从jQuery源代码转录出来的,它还向window.onload添加了一个回退侦听器。因此,在jQuery源代码中,第一次调用回调时需要删除侦听器,以避免为每个事件调用两次。但由于这段代码没有包含该回退,我认为没有必要删除侦听器。但我只是在推测。 - pgraham
如果您正在尝试复制 jQuery 2.x 的行为,则它不包括 document.attachEvent 的能力检测,并始终使用 DOMContentLoaded 事件。https://github.com/jquery/jquery/blob/master/src/core/ready.js - pgraham
@JanDvorak - 通常来说,删除不再需要的监听器是一个好的实践方法,可以提高性能。 - Jimbo Jonny
有人能确认在Mozilla Firefox中,domReady至少从第10行被调用了两次吗?很难理解为什么会这样。 - Manuel Arwed Schmidt

2

我不知道什么是原生JS,但你可以这样写:

document.getElementsByTagName('body')[0].className += ' javascript';

在页面底部(在关闭body标签之前)。

2
document.getElementsByTagName('body')[0] === document.body - alex
body 结束标签后也可以工作。甚至在 html 结束标签之后也可以。 - oriadam

1
如果您的目标是在页面加载时立即将类添加到body,可能是为了隐藏无JS回退元素,那么您可以在标签内立即执行此操作,而不必等待任何事件:
<body>
    <script type="text/javascript">
        document.body.className+= ' javascript';
    </script>

尽管一般来说,如果这是目的的话最好删除回退元素并逐步将其替换为脚本化元素,以便如果一个脚本出错,页面上的其他组件不会中断。

这是绑定元素的最快方式:在创建它们后立即执行(如果只需更改元素,则在打开标记内;如果需要更改其内容,则在关闭标记后)。然而,这种方法往往会在页面上散布丑陋的<script>块,这就是为什么更多的人将代码全部放在底部或使用加载/就绪处理程序的原因。


0
window.onload = function() {
   var elmt =  document.getElementsByTagName('body');
   if(elmt){
      elmt[0].className = 'javascript';
   }
}

应该可以了。

编辑:更新为按标签名获取元素而不是ID。


@David,谢谢你提醒。我不怎么用jQuery,所以以为那是通过ID查找元素。 - jaywon
2
我非常确定 onload 会在 $(document).ready 之后触发 - 我认为它在等待所有图片加载完成,例如。 - Kobi
2
document.body比gEBTN [0]更干净(并且稍微快一点)。 - Quentin

0

简单:

window.onload = function() {
  document.body.className = "javascript";
}

或者在HTML中:

<body onload="document.body.className = 'javascript'">...</body>

除非你想区分“onload之前”和“onload之后”,否则你可以静态地完成它:

<body class="javascript">...</body>

1
我认为他试图添加类来指示JavaScript已启用,因此您的第三个示例不太适用。而且,我很确定DOMReady会在Load之前触发(后者将等待图像和外部资源)。 - K Prime
请随意忽略第三个例子。公平地说,他没有表明他想通过那个例子实现什么。 - Eugene Lazutkin

0
你尝试过在文档的最后加入以下代码吗?
<script>
    document.body.className = 'javascript';
</script>

0

只需放置这个

<script>document.body.className += ' javascript';</script>

</body> 标签之前。简单易行(而且非常接近)的解决方案。


0
如果只涉及现代浏览器,您可以将此代码放在开头的body标签之后。
<script>
     document.body.classList.add("javascript");
</script>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接