如何让JavaScript模板字面量随变量更新?附实现代码
问题描述
我正在开发一个JavaScript网站应用,不需要跳转页面,点击特定链接后在主页面上方的容器加载对应内容,点击“X”关闭容器。链接的ID和存储内容的txt文件名对应,我用模板字符串生成文件路径,里面包含存储点击元素ID的变量。控制台显示ID已经成功读取到变量里,但模板字符串生成的路径不会跟着变量更新,导致加载不了对应内容。
想知道有没有办法让模板字符串随内部变量自动更新?或者有没有更好的实现方案?
HTML
<html> <head> <link rel="stylesheet" href="style.css"> </head> <body> <div class="sidePage"> <div class="sNav"> <ul class="sOptions"> <li><a id="p01btn" onclick="p01Op()">overview</a></li> <li><a id="p02btn" onclick="p02Op()">description</a></li> <li><a id="p03btn" onclick="p03Op()">credits</a></li> <li><a id="p04btn" onclick="p04Op()">share</a></li> </ul> <div class="sX" onclick="closeSidePage()"> <a>X</a> </div> </div> <div id="sContent"> <div id="p01" class="sClose">open 1</div> <div id="p02" class="sClose">open 2</div> <div id="p03" class="sClose">open 3</div> <div id="p04" class="sClose">open 4</div> </div> </div> <div class="main"> <h1>Links:</h1> <ul> <li> <a id="firstSidePage" onclick="openSidePage()">First Page</a> </li> <li> <a id="secondSidePage" onclick="openSidePage()">Second Page</a> </li> <li> <a id="thirdSidePage" onclick="openSidePage()">Third Page</a> </li> </ul> </div> <script src="app.js"></script> </body> </html>
CSS
* { padding: 0; margin: 0; } .main { height: 100%; width: 100%; z-index: 1; } .sidePage { height: 80%; width: 80%; position: fixed; z-index: 4; top: 10%; right: -100vw; background-color: rgb(33, 33, 33) } .sNav { width: 100%; display: flex; justify-content: space-around; height: 10%; position: absolute; bottom: 0; } .sOpen { display: block; opacity: 100%; color: white; } .sClose { display: none; opacity: 0%; } .sNav .sX { font-size: 40px; cursor: pointer; user-select: none; color: rgb(93, 206, 226); } .sNav ul li { list-style: none; font-size: 20pt; width: 15%; height: 100%; position: relative; color: rgb(93, 206, 226); } .sOptions { justify-content: space-around; align-items: center; display: flex; width: 80%; }
JavaScript
var sP = document.querySelector(".sidePage"); var sPp01 = document.getElementById('p01'); var sPp02 = document.getElementById('p02'); var sPp03 = document.getElementById('p03'); var sPp04 = document.getElementById('p04'); let sPactive = false; var sPopeners = document.querySelectorAll(".spOpener"); const sPcontent = document.getElementById("sContent"); var idString = ''; function sPopen(idHold) { idString = idHold.target.id; console.log(idString); } for (let i = 0; i < sPopeners.length; i++) { sPopeners[i].addEventListener('click', (idHold) => { sPopen(idHold); }, false); } let p01String = `text/p01/${idString}.txt`; let p02String = `text/p02/${idString}.txt`; let p03String = `text/p03/${idString}.txt`; let p04String = `text/p04/${idString}.txt`; function disappearDelay() { sPactive = false; sPp01.classList.remove("sOpen"); sPp01.classList.add("sClose") sPp02.classList.remove("sOpen"); sPp02.classList.add("sClose") sPp03.classList.remove("sOpen"); sPp03.classList.add("sClose"); sPp04.classList.remove("sOpen"); sPp04.classList.add("sClose"); } function closeSidePage() { sP.style.right = "-100vw"; setTimeout(() => { disappearDelay(); }, 2000); } function openSidePage() { sP.style.right = "10%"; sPactive = true; sPp01.classList.add("sOpen"); sPp01.classList.remove("sClose"); } function p01Output(data) { console.log(data); sPp01.innerHTML = this.responseText; } function p01Reader() { const p01Read = new XMLHttpRequest(); p01Read.addEventListener('load', p01Output); p01Read.open('GET', p01String); p01Read.send(); } if (sPactive = true) { p01Reader(); }; function p02Output() { sPp02.innerHTML = this.responseText; } function p02Reader() { const p02Read = new XMLHttpRequest(); p02Read.addEventListener('load', p02Output); p02Read.open('GET', p02String); p02Read.send(); } if (sPactive = true) { p02Reader(); }; function p03Output() { sPp03.innerHTML = this.responseText; } function p03Reader() { const p03Read = new XMLHttpRequest(); p03Read.addEventListener('load', p03Output); p03Read.open('GET', p03String); p03Read.send(); } if (sSactive = true) { p03Reader(); }; function p04Output() { sPp04.innerHTML = this.responseText; } function p04Reader() { const p04Read = new XMLHttpRequest(); p04Read.addEventListener('load', p04Output); p04Read.open('GET', p04String); p04Read.send(); } if (sSactive = true) { p04Reader(); }; function p01Op() { sPp01.classList.add("sOpen"); sPp01.classList.remove("sClose") sPp02.classList.remove("sOpen"); sPp02.classList.add("sClose") sPp03.classList.remove("sOpen"); sPp03.classList.add("sClose") sPp04.classList.remove("sOpen"); sPp04.classList.add("sClose"); } function p02Op() { sPp02.classList.add("sOpen"); sPp02.classList.remove("sClose") sPp01.classList.remove("sOpen"); sPp01.classList.add("sClose") sPp03.classList.remove("sOpen"); sPp03.classList.add("sClose") sPp04.classList.remove("sOpen"); sPp04.classList.add("sClose"); } function p03Op() { sPp03.classList.add("sOpen"); sPp03.classList.remove("sClose") sPp02.classList.remove("sOpen"); sPp02.classList.add("sClose") sPp01.classList.remove("sOpen"); sPp01.classList.add("sClose") sPp04.classList.remove("sOpen"); sPp04.classList.add("sClose"); } function p04Op() { sPp04.classList.add("sOpen"); sPp04.classList.remove("sClose") sPp02.classList.remove("sOpen"); sPp02.classList.add("sClose") sPp03.classList.remove("sOpen"); sPp03.classList.add("sClose") sPp01.classList.remove("sOpen"); sPp01.classList.add("sClose"); }
解决方案
核心问题
模板字符串是一次性求值的,你在全局定义p01String这些变量时,idString还是空值,所以生成的路径是text/p01/.txt,之后idString更新也不会改变已经生成的字符串。
优化方案
- 动态生成路径:不要提前定义路径变量,在需要请求文件时,用当前的
idString实时生成路径。 - 复用请求逻辑:把重复的请求代码封装成通用函数,减少冗余。
- 移除内联事件:把HTML里的内联
onclick改成事件绑定,便于维护。
优化后的JavaScript
const sP = document.querySelector(".sidePage"); const sContent = document.getElementById("sContent"); const sNavLinks = document.querySelectorAll(".sOptions a"); const closeBtn = document.querySelector(".sX"); const mainOpeners = document.querySelectorAll(".main ul a"); let currentPageId = ""; let sPactive = false; // 通用内容加载函数 function loadContent(contentId) { if (!currentPageId) return; const targetElement = document.getElementById(contentId); const filePath = `text/${contentId}/${currentPageId}.txt`; fetch(filePath) .then(response => { if (!response.ok) throw new Error(`加载失败: ${response.status}`); return response.text(); }) .then(text => { targetElement.innerHTML = text; }) .catch(err => console.error(err)); } // 切换内容显示状态 function switchContent(activeId) { // 批量隐藏所有内容 document.querySelectorAll("#sContent > div").forEach(el => { el.classList.remove("sOpen"); el.classList.add("sClose"); }); // 显示目标内容并加载数据 const activeEl = document.getElementById(activeId); activeEl.classList.add("sOpen"); activeEl.classList.remove("sClose"); loadContent(activeId); } // 打开侧边容器 function openSidePage(e) { currentPageId = e.target.id; sP.style.right = "10%"; sPactive = true; // 默认加载第一个内容 switchContent("p01"); } // 关闭侧边容器 function closeSidePage() { sP.style.right = "-100vw"; setTimeout(() => { sPactive = false; // 清空所有内容显示 document.querySelectorAll("#sContent > div").forEach(el => { el.classList.remove("sOpen"); el.classList.add("sClose"); }); }, 2000); } // 绑定事件 mainOpeners.forEach(opener => { opener.addEventListener("click", openSidePage); }); sNavLinks.forEach(link => { link.addEventListener("click", () => { const contentId = link.id.replace("btn", ""); // 从p01btn提取p01 switchContent(contentId); }); }); closeBtn.addEventListener("click", closeSidePage);
优化后的HTML(移除内联事件)
<html> <head> <link rel="stylesheet" href="style.css"> </head> <body> <div class="sidePage"> <div class="sNav"> <ul class="sOptions"> <li><a id="p01btn">overview</a></li> <li><a id="p02btn">description</a></li> <li><a id="p03btn">credits</a></li> <li><a id="p04btn">share</a></li> </ul> <div class="sX"> <a>X</a> </div> </div> <div id="sContent"> <div id="p01" class="sClose">open 1</div> <div id="p02" class="sClose">open 2</div> <div id="p03" class="sClose">open 3</div> <div id="p04" class="sClose">open 4</div> </div> </div> <div class="main"> <h1>Links:</h1> <ul> <li> <a id="firstSidePage">First Page</a> </li> <li> <a id="secondSidePage">Second Page</a> </li> <li> <a id="thirdSidePage">Third Page</a> </li> </ul> </div> <script src="app.js"></script> </body> </html>
额外建议
- 用
fetch替代XMLHttpRequest,语法更简洁,异步处理更清晰。 - 避免重复DOM操作,用循环批量处理元素类名。
- 变量命名尽量语义化,比如
currentPageId比idString更直观。 - 可以用事件委托进一步优化,比如给
.sOptions绑定单个事件,通过event.target判断点击的链接,减少事件绑定数量。
内容的提问来源于stack exchange,提问作者Dominic Merlo
相关产品推荐
相关产品推荐

