【问题标题】:Is it possible to automatically declare thousands of variables in JavaScript?是否可以在 JavaScript 中自动声明数千个变量?
【发布时间】:2019-02-10 18:55:23
【问题描述】:

我对 JavaScript 比较陌生,所以我不确定我在这里做的事情是否传统,是否有更好的方法来做我想做的事情。

我有一个 JavaScript 函数,它从 JSON 文档中提取大约 3,600 个句子,并将它们自动插入到我的 HTML 代码中。在 HTML 中为每一次生成一个唯一的 id。

我想为每个句子创建一个 onclick 事件,以便在单击它时在该句子下方显示更多信息。这意味着我必须声明数千个变量,一个用于每个句子,一个用于与该句子关联的每个信息 div:

var sent1 = document.getElementById('s1');
var sent1info = document.getElementById('s1info');
var sent2 = document.getElementById('s2');
var sent2info = document.getElementById('s2info');
var sent3 = document.getElementById('s3');
var sent3info = document.getElementById('s3info');
...

手动操作太多了。有没有办法自动化声明这些变量的过程,或者有更好的方法来做我正在做的事情?

对于上下文,我对每个变量的意图是将其输入到这个函数中:

sent1.onclick = function(){
    if(sent1info.className == 'open'){
        sent1info.className = 'close';
    } else{
        sent1info.className = 'close';
    }
};

从这里开始,当 className 为 'close' 时,CSS 会将信息框缩小到 0 的高度,并在 className 为 'open' 时展开它。但是,同样,这将需要我写出这个函数数千次。

有没有办法自动执行此操作?还是我做错了?

编辑以显示 HTML:

<!DOCTYPE html>
<html>
<head>...</head>
<body>
    <div id="everything">
        <header id="theheader" class="clearfix">...</header>
        <div id="thebody" class="box clearfix">
            <aside id="page" class="side">...</aside>
            <div class="items">
                <article id="content" class="article">
                    <img id="sentpic" src="sentpic.jpg">
                    <h1>Sentences</h1>
                    <div id="sentences">
                        *** This is where the JS inserts sentences and information ***
                        <ul id='sent1' class='sentcontent'><li class='number'>1.</li><li class='thesent'>...</li></ul>
                        <div id='sent1info' class='infobox'>
                            <ul class='sentinfo'><li class='information'>Info:</li><li class='infotext'><em>...</em></li></ul>
                            <ul class='sentinfo'><li class='information'>Line:</li><li class='line'>...</li></ul>
                        </div>
                        <ul id='sent2' class='sentcontent'><li class='number'>2.</li><li class='thesent'>...</li></ul>"
                        <div id='sent2info' class='infobox'>
                            <ul class='sentinfo'><li class='information'>Info:</li><li class='infotext'><em>...</em></li></ul>
                            <ul class='sentinfo'><li class='information'>Line:</li><li class='line'>...</li></ul>
                        </div>
                        *** it goes on like this for each sent inserted ***
                    </div>
                </article>
            </div>
        </div>
        <div class="associates clearfix">...</div>
        <footer class="foot">...</footer>
    </div>
    <script type="text/javascript" src="index.js"></script>
</body>
</html>

【问题讨论】:

  • 为什么不使用 forEach 或 for 循环?
  • 只需将具有共同类的元素索引到句子数组中的索引即可。展示如何从数据中生成 html
  • 这将有助于查看创建的 HTML 结构。例如:句子和细节是否在同一个父级中?它们在页面的其他地方吗?另外,生成这个样子的函数是什么?如果您编辑问题以包含这些内容,您可能会得到更好的答案。
  • 您能否展示一下您当前使用的 HTML 片段?

标签: javascript html css json


【解决方案1】:

使用 HTML &lt;details&gt; 元素:

const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

const template_sentence = (ob, i) => `
<details class="sentence">
  <summary>${i+1} ${ob.thesent}</summary>
  <h3>${ob.info}</h3>
  <div>${ob.line}</div>
</details>`;

document.querySelector("#sentences").innerHTML = json.map(template_sentence).join('');
&lt;div id="sentences"&gt;&lt;/div&gt;

否则,使用您当前的非语义标记:

不需要按 ID 定位(在您的特定情况下)。还有其他方法,例如 CSS 中的 + Next Adjacent 兄弟选择器。

这是一个 JS 示例 - 应该是不言自明的,但请随时提问。

  • 使用 JS 将类(本例中为 .active)切换到可点击的 UL 元素
  • 使用 CSS 和 Next 相邻兄弟选择器 + 来制作 info DIV display: block

/* Just a sample... you'll know how to modify this with the right properties I hope */
const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

// The toggle function:
const toggleInfobox = ev => ev.currentTarget.classList.toggle("active");

// A single sentcontent template
const template_sentence = (ob, i) =>
`<ul class='sentcontent'>
    <li class='number'>${i+1}</li>
    <li class='thesent'>${ob.thesent}</li>
  </ul>
  <div class='infobox'>
    <ul class='sentinfo'>
      <li class='information'>Info:</li>
      <li class='infotext'><em>${ob.info}</em></li>
    </ul>
    <ul class='sentinfo'>
      <li class='information'>Line:</li>
      <li class='line'>${ob.line}</li>
    </ul>
</div>`;

// Get target element
const el_sentences = document.querySelector("#sentences");

// Loop JSON data and create HTML
el_sentences.innerHTML = json.map(template_sentence).join('');

// Assign listeners
const el_sentcontent = el_sentences.querySelectorAll(".sentcontent");
el_sentcontent.forEach(el => el.addEventListener('click', toggleInfobox));
/* BTW, why do you use <ul> ? That's not a semantic list! */
.sentcontent { padding: 0; cursor: pointer;}
.sentcontent li { display: inline-block; }

/* Arrows are cool, right? */
.sentcontent:before        { content: "\25BC"; }
.sentcontent.active:before { content: "\25B2"; }

/* Hide adjacent .infobox initially, 
/* and show adjacent .infobox on JS click */
.sentcontent        + .infobox { display: none; }
.sentcontent.active + .infobox { display: block; }
&lt;div id="sentences"&gt;&lt;/div&gt;

在这个Stack overflow answer 中,您可以了解更多关于在单击某个按钮时切换元素的信息。

【讨论】:

  • ID 是肯定通常不是没用的(不确定你是否在暗示,但似乎是这样)。对于某些可访问性要求,ID 是绝对必要的。
  • @AndyHoffman 我同意 ID 在需要查询参数化锚点以允许滚动到喜欢时很有用://example.com#sent1//example.com#sent1content 但答案,也不是问题暗示这种行为- 不要谈论describedby aria 等...... - 这是这个答案的另一个扩展 - 也许如果 OP 对这个话题感兴趣......
  • 是的,但是您对 ID 做了一般性声明,考虑到您在这里的排名,可能会传播不好的建议。
  • 由于浏览器从右到左匹配CSS选择器,这行:.sentcontent + .infobox { display: none; }可以优化为.infobox { display: none; }
  • @AndyHoffman 是的......作为一般说明......但封装行为很重要。 .infobox 可能出现在 DOM 的其他地方。我们不想display: none; 那些元素,只有那些在某些时候通过兄弟选择的精确重聚来处理的元素 v: .sentcontent + .infobox.sentcontent.active + .infobox - 关注点分离等。一个很大的话题。所以不行。在我看来,建议绝对是首选。
【解决方案2】:

这个问题更多的是架构问题,而不是创建动态变量的需要。考虑这个例子:

  • ids 被移除(使用现有的类名)
  • 此模式适用于n 句子实例
  • handleClick 中,我们在单击的元素上切换open 类,这让我们可以通过CSS 使用相邻的兄弟选择器
  • 不需要close 类,因为没有open 类代表关闭状态。

let outerUL = document.querySelectorAll('.sentcontent')

function handleClick() {
  this.classList.toggle('open');
}

outerUL.forEach(ul => {
  ul.addEventListener('click', handleClick);
})
.sentcontent {
  cursor: pointer;
}

.sentcontent.open + .infobox {
   display: block;
}

.infobox {
  background-color: #eee;
  display: none;
  padding: .25em .5em;
}
<ul class='sentcontent'>
  <li class='number'>1.</li>
  <li class='thesent'>Sent</li>
</ul>
<div class='infobox'>
  <ul class='sentinfo'>
    <li class='information'>Info</li>
    <li class='infotext'><em>Info text</em></li>
  </ul>
  <ul class='sentinfo'>
    <li class='information'>Line info</li>
    <li class='line'>Line</li>
  </ul>
</div>

<ul class='sentcontent'>
  <li class='number'>2.</li>
  <li class='thesent'>Sent</li>
</ul>
<div class='infobox'>
  <ul class='sentinfo'>
    <li class='information'>Info</li>
    <li class='infotext'><em>Info text</em></li>
  </ul>
  <ul class='sentinfo'>
    <li class='information'>Line info</li>
    <li class='line'>Line</li>
  </ul>
</div>

https://jsfiddle.net/d91va7tq/2/

【讨论】:

  • 安迪,现在看起来很酷! (PS:最好不要将类切换到 DIV 元素,而是将类切换到目标/触发元素!这样我们不仅可以使用 + 运算符控制 DIV display,还可以更改触发器的样式!)干得好
  • @RokoC.Buljan 好建议。完成。
  • @AndyHoffman 我很难让它工作。我需要在某个地方加载吗?当我测试它时,一切都在加载,但点击实际上并没有做任何事情。
  • @AdeDoyle 将 JavaScript 放在标记底部的 &lt;/body&gt; 之前:&lt;script&gt;js code here&lt;/script&gt;
  • 使用最后一个修复按预期工作。完美的。以防万一将来对其他人有帮助,我不得不将这个答案中的 JS 组合成一个自己的函数,并在插入后立即在动态创建句子的函数末尾调用该函数进入 HTML。
【解决方案3】:

当你有一个非常大的 json 数据时,最好记住不要一次渲染整个数据,它会影响 webbrowser 的性能。而是在需要时渲染。这就是用户单击以获取更多信息的时候。

我在下面做了一些例子,请务必阅读评论

const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

const container = document.querySelector(".container");
json.forEach((item)=> {
let x= item;
let el = document.createElement("li");
el.innerHTML = x.thesent;
container.appendChild(el);
el.addEventListener("click",()=> {
var infoContainer= el.querySelector(".info");
// dont create all html element at once, instead create them 
//when the user click on it. this is better when you have a very large data.
if (!infoContainer){ // not created, then create  
    infoContainer = document.createElement("div");
    infoContainer.className="info";
    var info = document.createElement("div");
    var line = document.createElement("div");
    info.innerHTML = x.info;
    line.innerHTML = x.line;
    infoContainer.appendChild(info);
    infoContainer.appendChild(line);
    el.appendChild(infoContainer);
} else if (infoContainer.style.display == "none") // created and hidden, then display it 
           infoContainer.style.display = "block";
  else infoContainer.style.display= "none"; // already displayed then hide it 
});
})
.container li >div.info >div:first-child{
font-size: 12px;

}

.container li >div.info >div:last-child{
font-size: 10px;

}
<ul class="container">

</ul>

【讨论】:

  • 不完全是 OP 使用的标记。他使用 UL(以一种奇怪的方式,但是是的...... :) - 关于 在需要时渲染 的好主意!
  • 我正在复制我在标题中制作导航菜单的方式。我现在意识到我应该一直使用表格格式(是吗?),但我对 JS 不是很好,这在技术上是可行的。我可能会将此作为修复的一部分。
  • 谢谢@Roko C. Buljan,在这类事情上积累了一些经验。关于markup,无论如何他都需要重写他的。所以我想让他明白正确的方法或好的方法。
  • 很高兴听到它对您有帮助。但是您知道使用 jquary 或 vue 可以使渲染数据变得更加简单。使用表格并不是更好,您无法随心所欲地控制设计。使用 divs 或 ul 会好得多,但我认为它是 agen 。最后别忘了投票:)
  • 不幸的是,不能使用 jquery、vue 等。在投票之前,我仍在努力解决问题,但感谢我得到的所有有用信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多