我有这个代码:
功能loader()
将继续运行,直到<body>
确实存在。当<body>
确实存在时,插入.loader
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
loader();
function loader() {
if (!$('body').length) {
window.requestAnimationFrame(loader);
} else {
$('<div class="loader"></div>').prependTo('body');
}
};
</script>
</head>
<body>
<!-- elements -->
</body>
</html>
我想问这一行
if (!$('body').length) {}
.length
用什么来确定它的回报,是<body>
还是<body></body>
? .length
是否在等待关闭标签</body>
?如果是的话,我的替代方案是什么?我需要插入.loader
而不必等待</body>
。
我的最后一招是
<body>
<div class="container"></div>
<!-- rest of elements -->
</body>
那么
if (!$('.container').length) {}
但我需要不影响元素结构的替代品。
编辑:
我重新提出问题并添加以下代码。
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function(){
console.log("ready timestamp");
});
loader();
function loader() {
if (!$('body').length) {
window.requestAnimationFrame(loader);
} else {
console.log("length timestamp");
}
};
</script>
</head>
<body>
<!-- elements -->
</body>
</html>
输出:
15:03:19.862 length timestamp
15:03:20.198 ready timestamp
长度时间戳始终低于就绪时间戳。
经测试:
Chrome 71.0和FF 65.0
在控制台设置中启用时间戳(开发人员工具 - >控制台 - >显示时间戳)。
其他人可能会问我为什么这样做,300ms左右是什么?
我实现PWA并需要加载器,如下所示:Showing loader while navigating between pages - PWA。问题是,上一页的.on('beforeunload')
与下一页的$()
之间有300毫秒。显然,这是个昙花一现。
这个blip可以理想地/容易地通过服务器端脚本解决,但正如我上面提到的,我无法访问服务器端脚本,我可以问的最远的是一个空白元素<div class="container"></div>
。
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
console.log( $('#tryToFindMe').length );
</script>
<div id="tryToFindMe"></div>
与您的问题在定义之前尝试访问主体的方式类似,此代码段尝试在定义之前访问div。在这两种情况下,都不会找到该元素。
这是因为当您的浏览器将您的网页解析为文档对象模型时,它会从上到下进行解析。因此,在脚本运行时,将在DOM中创建div的html尚未处理。因此脚本尝试查找元素,它不存在,长度为零。
这就是为什么使用jQuery文档准备好,绑定到DOMContentLoaded事件或load事件的原因。这些不同的方法推迟了逻辑的执行,直到整个页面被解析为DOM,或者在加载的情况下,不仅页面已被解析到DOM中,而且所有资源都已被接收(图像,视频等)。如果没有这些方法,脚本将需要在定义元素后出现在页面中,以确保元素已存在于DOM中。
在考虑与DOM交互时,重要的一点是不要用HTML来思考。 DOM包含节点,而不是HTML。节点是从HTML生成的。
您可以像这样使用jQuery文档就绪函数($(() => {})
):
$(() => {
$(`<div class="loader"></div>`).prependTo("body");
});
我跑了另一个测试。
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function(){
console.log("ready timestamp");
});
loader_bottom();
loader_body();
loader_top();
function loader_bottom() {
if (!$('.container_bottom').length) {
window.requestAnimationFrame(loader_bottom);
} else {
console.log("container_bottom.length timestamp");
}
};
function loader_top() {
if (!$('.container_top').length) {
window.requestAnimationFrame(loader_top);
} else {
console.log("container_top.length timestamp");
}
};
function loader_body() {
if (!$('body').length) {
window.requestAnimationFrame(loader_body);
} else {
console.log("body.length timestamp");
}
};
</script>
</head>
<body>
<div class="container_top"></div>
<!-- elements between -->
<div class="container_bottom"></div>
</body>
</html>
输出:
09:09:16.894 body.length timestamp
09:09:16.897 container_top.length timestamp
09:09:17.090 container_bottom.length timestamp
09:09:17.220 ready timestamp
因为$('.container_top')
与$('body')
相邻,改变了序列,
loader_top();
loader_body();
毫不奇怪的输出:
09:09:39.708 container_top.length timestamp
09:09:39.708 body.length timestamp
因此我得出结论:
.length
是否准备好DOM?是的,它确实。.length
用什么来确定它的回报,是<body>
还是<body></body>
?这是<body>
。虽然第二个问题的正确短语应该是:DOM Parser用什么来确定节点,是<tag>
还是<tag></tag>
?答案是<tag>
。
实际上,我并不感到惊讶,我们可以在它准备好之前操纵DOM。此外,$().ready()
只是DOM准备好的事件。 Javascript是多线程所以(我认为)DOM操作应该可以与DOM解析并行运行。直到这段代码我才能证明这一点。