Vanilla JS单页应用跨路由元素访问及回车事件绑定问题
问题:Vanilla JS SPA中跨路由绑定Enter按键事件
在Vanilla JavaScript单页应用(SPA)中,/login路由包含email、password输入框及登录按钮,希望用户按下Enter键即可提交数据,且不想使用form表单。但在首页(/)中绑定Enter Keypress事件监听器时,控制台提示element is null,原因是/login路由的内容尚未加载到首页DOM中。
提供的代码片段
首页(/)代码
<!DOCTYPE html> <html> <head> <title>Vanilla SPA Router</title> <meta charset="UTF-8" /> <link rel="stylesheet" href="css/styles.css" /> <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.min.css" rel="stylesheet" /> </head> <body> <div id="root"> <nav id="main-nav" class="sidebar"> <a href="/" onclick="route()">Home</a> <a href="/about" onclick="route()">About</a> <a href="/lorem" onclick="route()">Lorem</a> <a href="/login" onclick="route()">login</a> </nav> <div id="main-page"></div> </div> <script> // 此处绑定事件会报错,因为password元素不存在 var input = document.getElementById("password"); input.addEventListener("keypress", function(event) { if (event.key === "Enter") { event.preventDefault(); document.getElementById("login_btn").click(); } }); // login()函数未包含,不影响问题 </script> <script src="router.js"></script> </body> </html>
/login路由代码
<input type="email" id="email" placeholder="email" /> <input type="password" placeholder="password" id="password" /> <button onClick="login()" id="login_btn" className="btn btn-primary">Login</button>
路由核心代码(router.js)
const route = (event) => { event = event || window.event; event.preventDefault(); window.history.pushState({}, "", event.target.href); handleLocation(); }; const routes = { 404: "/pages/404.html", "/": "/pages/index.html", "/about": "/pages/about.html", "/lorem": "/pages/lorem.html", "/login": "/pages/login.html", }; const handleLocation = async () => { const path = window.location.pathname; const route = routes[path] || routes[404]; const html = await fetch(route).then((data) => data.text()); document.getElementById("main-page").innerHTML = html; }; window.onpopstate = handleLocation; window.route = route; handleLocation();
解决方案
方案1:路由切换完成后绑定事件
修改路由的handleLocation函数,在页面HTML插入DOM后,判断当前路径为/login时再绑定事件,确保元素已存在:
const handleLocation = async () => { const path = window.location.pathname; const route = routes[path] || routes[404]; const html = await fetch(route).then((data) => data.text()); document.getElementById("main-page").innerHTML = html; // 仅在/login路由加载后绑定事件 if (path === "/login") { const passwordInput = document.getElementById("password"); if (passwordInput) { // 先移除旧监听器避免重复绑定 passwordInput.removeEventListener("keypress", handleEnterSubmit); passwordInput.addEventListener("keypress", handleEnterSubmit); } } }; // 单独提取事件处理函数 function handleEnterSubmit(event) { if (event.key === "Enter") { event.preventDefault(); document.getElementById("login_btn")?.click(); } }
方案2:全局事件委托(推荐)
利用事件冒泡机制,在根元素上绑定一次全局事件,通过判断触发元素的标识和当前路由,实现跨路由的事件监听:
// 在首页脚本或router.js中添加 document.getElementById("root").addEventListener("keypress", function(event) { // 校验当前路由和触发元素 if (window.location.pathname === "/login" && event.target.id === "password") { if (event.key === "Enter") { event.preventDefault(); document.getElementById("login_btn")?.click(); } } });
这种方式只需绑定一次事件,无需在每次路由切换时重复操作,适合SPA全局交互管理。
方案3:在login页面内直接绑定事件
将事件绑定逻辑内嵌到login.html中,当页面被插入DOM时自动执行,确保元素已加载:
<input type="email" id="email" placeholder="email" /> <input type="password" placeholder="password" id="password" /> <button onClick="login()" id="login_btn" className="btn btn-primary">Login</button> <script> const passwordInput = document.getElementById("password"); passwordInput.addEventListener("keypress", function(event) { if (event.key === "Enter") { event.preventDefault(); document.getElementById("login_btn").click(); } }); </script>
这种方式让页面交互逻辑与页面本身耦合,便于独立维护。
内容的提问来源于stack exchange,提问作者John Sall
相关产品推荐
相关产品推荐

