如何修改JavaScript使DOM操作脚本支持通过外部JS文件加载
实现全外部JS加载的方案
你遇到的报错根本原因是:外部JS加载执行时,页面DOM树还未完全解析,调用document.getElementById获取未渲染的元素会返回null,后续调用方法自然报错。可以按以下步骤改造:
第一步:清理HTML结构,移除所有内嵌JS
把原来HTML里的内嵌脚本全部删除,document.write逻辑直接替换为静态DOM节点,改造后的HTML如下:
<div id="wrapper"> <p><img style="width: 100%;" id="idBild" src="http://placehold.it/350x150" alt="Image"></p> <p> <input id="idRueck" type="button" value="<"> <input id="idVor" type="button" value=">"> <span id="idAusgabe"></span> </p> <p> <input id="idStart" type="button" value="Start"> <input id="idStop" type="button" value="Stop"> </p> </div> <!-- 外部JS放在body末尾,保证执行时DOM已解析完成 --> <script src="gallery.js"></script>
如果要把JS放在<head>里,给script标签加defer属性即可:<script src="gallery.js" defer></script>,同样能保证DOM解析完成后再执行JS。
第二步:合并所有逻辑到外部JS文件gallery.js
所有DOM操作都封装到DOM加载完成的回调中,完整代码如下:
// 全局变量定义 const bild = ["http://placehold.it/350x150", "http://placehold.it/350x150", "http://placehold.it/350x150","http://placehold.it/350x150", "http://placehold.it/350x150"]; let bildIndex = 0, timeoutVerweis, timeoutAktiv = false; // 核心功能函数 function move(zahl) { bildIndex = zahl; if(bildIndex >= bild.length) bildIndex = 0; else if(bildIndex < 0) bildIndex = bild.length - 1; document.getElementById("idBild").src = bild[bildIndex]; document.getElementById("idAusgabe").firstChild.nodeValue = (bildIndex+1) + "/" + bild.length; if(timeoutAktiv) timeoutVerweis = setTimeout( function() {move(bildIndex+1);}, 2000); } function start() { if(timeoutAktiv) return; timeoutAktiv = true; timeoutVerweis = setTimeout( function() {move(bildIndex+1);}, 2000); } function stop() { clearTimeout(timeoutVerweis); timeoutAktiv = false; } // DOM解析完成后执行绑定和初始化 document.addEventListener('DOMContentLoaded', function() { // 初始化计数显示 document.getElementById("idAusgabe").innerText = `1/${bild.length}`; // 绑定按钮事件 document.getElementById("idRueck").addEventListener("click", function() { move(bildIndex - 1); } ); document.getElementById("idVor").addEventListener("click", function() { move(bildIndex + 1); } ); document.getElementById("idStart").addEventListener("click", start ); document.getElementById("idStop").addEventListener("click", stop ); // 绑定鼠标悬停事件 const wrapper = document.getElementById("wrapper"); wrapper.addEventListener("mouseover", function() { clearTimeout(timeoutVerweis); timeoutAktiv = false; }, false); wrapper.addEventListener("mouseout", function() { if(timeoutAktiv) return; timeoutAktiv = true; timeoutVerweis = setTimeout(function() {move(bildIndex+1);}, 500); }, false); // 初始化自动轮播 if(!timeoutAktiv) { timeoutAktiv = true; timeoutVerweis = setTimeout(function() {move(bildIndex+1);}, 2000); } })
更优的优化方案
- 避免全局变量污染:可以把所有逻辑封装到一个IIFE(立即执行函数)或者ES6 Class中,所有变量只在闭包内生效,不会和页面其他脚本冲突
- 事件委托:如果后续有动态新增节点的需求,可以把事件绑定在父级
#wrapper上,不用给每个按钮单独绑定事件 - 配置抽离:把轮播间隔、图片列表、元素ID等作为配置项抽离,后续修改不用改动核心逻辑
内容的提问来源于stack exchange,提问作者knefie
相关产品推荐
相关产品推荐

