You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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更新也不会改变已经生成的字符串。

优化方案

  1. 动态生成路径:不要提前定义路径变量,在需要请求文件时,用当前的idString实时生成路径。
  2. 复用请求逻辑:把重复的请求代码封装成通用函数,减少冗余。
  3. 移除内联事件:把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:00:58