简单的方法将元素关联起来,并在单击按钮时显示正确的元素



我正在尝试构建一个餐厅菜单。最初,用户将只看到带有每个部分标题的div元素,当单击其中一个时,应向用户显示相关的菜肴。我将分享我尝试的解决方案,但我觉得我这样做的方式很复杂。如果你读过这篇文章,知道更好的方法来实现我正在努力做的事情,请分享。

简单解释一下html:菜单中的每个.section(即开胃菜、主菜(都是一个div,该部分的每个条目(即油炸Calmari、Mozzarella Sticks(都包含.section-headerdiv.entrydiv。Html:

<div class="section">
<div class="section-header appetizers">Appetizers</div>
<div class="entry appetizers">
<div class="name-of-food">Stuffed Mushrooms</div>
<div class="description"></div>
<div class="price">$6.95</div>
</div>
<div class="entry appetizers">
<div class="name-of-food">Zucchini Sticks</div>
<div class="description">Served with marinara sauce</div>
<div class="price">$6.95</div>
</div>
</div>

正如您所看到的,我(使用js(使每个.section.section-headerdiv和.entrydiv都共享一个公共类(在本例中为.appetizers(。我这样做是为了捕捉合适的类,以便在上应用display:block

let sectionHeaders = document.getElementsByClassName('section-header'); //
for(i = 0; i < sectionHeaders.length; i++){
sectionHeaders[i].addEventListener('click', function(){
// Right now this.className = 'section-header appetizers'(or entrees, etc).
// by splitting at the " " it creates an array equal to ['section-header', 'appetizers'].
// The pop() method then removes the last element and returns it. Leaving targetClass equal to
// 'appetizers'.
let targetClass = this.className.split(" ").pop(); // only elements that should be redsiplayed will have this class
//After this I was going to try and target all div elements with targetClass and apply display:block; to those but stopped because I feel I am doing this wrong.
}
)}; 

这种寻找并重新显示适当.entry元素的方法似乎是错误的。除了我写的用正确的类名生成所述元素的代码之外(我不认为这是否重要,它很长,但我会把它放在下面以防万一(,我觉得我的方法完全错误。不管怎样,如果你读了所有这些,并有一个简单的解决方案,请提前分享并感谢!

js:

let menu = [
[
'Appetizers',
['Stuffed Mushrooms', "", '$6.95'],
['Zucchini Sticks', "Served with marinara sauce", '$6.95']
],
[
'Soups',
['Chicken Noodle','','$4.95'],
['Minestrone','','$4.95'],
['Lentil','','$5.95'],
['Pasta Fagioli','','$5.95'],
['Cheese Tortellini','','$6.95'],
]
]
//Generate Table of Contents
tocUlElement = document.getElementById('toc-list'); //gets ul element. append li elements to this element
tocItems = generateTOCItemsAsArray(menu); // i.e ['Appetizers', 'Entrees', 'Desserts']
for(i = 0; i < tocItems.length; i++){
liElement = document.createElement("li"); //create list item element
liElement.classList.add("toc-list-item"); //give class to list item element
aElement = document.createElement('a'); //create link element
aElement.innerHTML = tocItems[i]; // give link text
aElement.setAttribute('href', '#'); //leave href blank for now
liElement.appendChild(aElement); //append link to list item
tocUlElement.appendChild(liElement); //append list item to ul
}
//Generate Menu
let menuDiv = document.getElementById('menu') // represents entire menu
for(i = 0; i < menu.length; i++){
let section = document.createElement('div'); // one section of the menu (i.e Appetizers)
section.classList.add('section');
for(j = 0; j < menu[i].length; j++){
if(j == 0){ // menu[i][0] is a string representing the name of the section i.e 'Appetizers'
let sectionHeader = document.createElement('div'); // will contain heading to be displayed if user is on desktop and if user is on mobile
sectionHeader.classList.add('section-header');
sectionHeader.classList.add(menu[i][0].toLowerCase()); // button will share this class with each entry element so that the proper ones can be shown when button is clicked
sectionHeader.innerHTML = menu[i][j]; // i.e 'Appetizers', 'Entrees', etc
section.appendChild(sectionHeader); // append .section header to entire section (.section)
}
else{ //Every element after menu[i][0] is is an array representing a menu entry in the form of [food, description, price]
let entry = document.createElement('div'); // div represents a single entry, it consits of 3 divs, one for the name of the food, description and price
entry.classList.add('entry');
entry.classList.add(menu[i][0].toLowerCase());
let nameOfFood = document.createElement('div'); // These three divs will be appended to the entry div
let description = document.createElement('div');
let price = document.createElement('div');
nameOfFood.classList.add('name-of-food');
description.classList.add('description');
price.classList.add('price');
nameOfFood.innerHTML = menu[i][j][0]; // Remember [food, description, price]
description.innerHTML = menu[i][j][1];
price.innerHTML = menu[i][j][2];
entry.appendChild(nameOfFood); //
entry.appendChild(description);
entry.appendChild(price);
section.appendChild(entry); // Append the entry to the section
} // Repeat this process for every entry remaining in the current array item, ex first iteration is at menu[0]
menuDiv.appendChild(section); // Append section to the menu
} // move on to next item in menu variable. Second iteration would be menu[1]
}

let sectionHeaders = document.getElementsByClassName('section-header'); //
for(i = 0; i < sectionHeaders.length; i++){
sectionHeaders[i].addEventListener('click', function(){
// Right now this.className = 'section-h2 appetizers'(or entrees, etc).
// by splitting at the " " it creates an array equal to ['section-h2', 'appetizers'].
// The pop() method then removes the last element and returns it. Leaving targetClass equal to
// 'appetizers'.
let targetClass = this.className.split(" ").pop();
//After this I was going to try and target all divs with targetClass 
// and display those. Stopped doing this because I feel I am doing it wrong.
}
)};
/* Function definitions */
function generateTOCItemsAsArray(menu){
let tocItemsAsArray = []
for(i = 0; i < menu.length; i++){
tocItemsAsArray.push(menu[i][0]);
}
return tocItemsAsArray;
}

有很多方法可以做到这一点。我来解释3。

我可以想到的一种方法是使用数据属性。它们的存在正是出于这样的原因:添加要使用JavaScript检索的数据。

<div class="section">
<div data-type="appetizers" class="section-header appetizers">Appetizers</div>
<div class="entry appetizers">
<div class="name-of-food">Stuffed Mushrooms</div>
<div class="description"></div>
<div class="price">$6.95</div>
</div>
<div class="entry appetizers">
<div class="name-of-food">Zucchini Sticks</div>
<div class="description">Served with marinara sauce</div>
<div class="price">$6.95</div>
</div>
</div>

查看如何将data-type属性添加到.section-header元素中。然后,在JavaScript中,您可以使用以下内容检索它:

for (let sectionHeader of document.getElementsByClassName('section-header')) { // ES6 way of looping an array. Kinda cool ;-)
sectionHeader.addEventListener('click', () => {
let type = this.dataset.type;
console.log(type); // This will show the `data-type` attribute value
});
}

查看如何使用.dataset属性访问data-type值。这里有更多关于这方面的文档:https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/dataset

顺便说一句,因为你正在使用JavaScript来创建元素和菜单(作为个人观点,有一种很酷的可配置菜单的方式。这也是我通常的做法(,你已经引用了所有内容,所以你实际上不需要data-*之类的东西。我走这边:

for (let i = 0; i < menu.length; i++) { // Important, don't forget the `let` keyword when you define a `i` variable in a loop.
let section = document.createElement('div');
section.classList.add('section');
(function () {
// This anonymous function will encapsulate loop scope. Important in this case as we are going to define a callback inside the loop that will access parent scope.
let entries = []; // Create an entries array
for (let j = 0; j < menu[i].length; j++) {
if (j == 0) {
let sectionHeader = document.createElement('div');
// Now that we already have a reference to sectionHeader, attach the 'click' event listener and do our work
sectionHeader.addEventListener("click", function() {
// This is where we have the callback I said before. Will access the parent scope "entries" array, which is independent for each loop
for (let entry of entries) {
entry.doWhatEverYouWant();
}
});
/*[...]*/
} else {
let entry = document.createElement('div');
entries.push(entry); // Add this entry to the entries array
/*[...]*/
}
}
})(); // Note how I call the anonymous function with this weird })(); so it runs just when is created. Is like creating a function named pepe and then calling pepe() just after creating it.
menuDiv.appendChild(section);
}

我能想到的第三种方法是,因为每个列表都封装在自己的section中,所以很容易访问它的.section.entry来选择你想要的元素:

<div class="section">
<div class="section-header appetizers">Appetizers</div>
<div class="entry appetizers">
<div class="name-of-food">Stuffed Mushrooms</div>
<div class="description"></div>
<div class="price">$6.95</div>
</div>
<div class="entry appetizers">
<div class="name-of-food">Zucchini Sticks</div>
<div class="description">Served with marinara sauce</div>
<div class="price">$6.95</div>
</div>
</div>

HTML与您的完全相同。但是节头和条目之间存在差异。Tne条目具有entry类,那么您可以执行:

let sectionHeader = document.createElement('div'); // Or whatever way you want as long as is a reference to the section header, as we said before. You can still use  document.getElementsByClassName('section-header') or whatever you feel more comfortable.
// Now attach the click listener, as always
sectionHeader.addEventListener("click", function() {
// Here comes the magic. You can simple access the parent node...
let section = this.parentNode;
//...which will be the <div class="section"> element
// And now you can select the `.entry` elements and work with them
for (let entry of section.querySelectorAll(".entry")) {
entry.doWhateverYouWant();
}
});

正如您所看到的,我不是在整个document上调用querySelectorAll(或getElementsByClassName(,而是在.section元素上调用它,因此只会在该元素内部找到.entry元素,而不会向外调用。

我可以想出更多的方法,但更有可能是这个3的变体。有了这个3,你会有一些想法。

我会将属于容器元素中的条目分组。然后这个容器元素将紧跟在header元素之后,您可以切换该容器元素的显示。

此外,我会在整个菜单上定义点击处理程序,然后检查点击的元素是否是标题。这样就不必定义多个单击处理程序。

定义一个hidden类以轻松切换可见性。

关于你的代码的一些备注:

  • 不要使用innerHTML来分配纯文本。使用textContent
  • 始终声明变量。值得注意的是,for循环变量不应该是全局的

以下是具有这些自适应功能的代码。有些评论是我修改的。

let menu = [
[
'Appetizers',
['Stuffed Mushrooms', "", '$6.95'],
['Zucchini Sticks', "Served with marinara sauce", '$6.95']
],
[
'Soups',
['Chicken Noodle','','$4.95'],
['Minestrone','','$4.95'],
['Lentil','','$5.95'],
['Pasta Fagioli','','$5.95'],
['Cheese Tortellini','','$6.95'],
]
]
let tocUlElement = document.getElementById('toc-list');
let tocItems = generateTOCItemsAsArray(menu);
for(let i = 0; i < tocItems.length; i++) {
let liElement = document.createElement("li");
liElement.classList.add("toc-list-item");
let aElement = document.createElement('a');
aElement.textContent = tocItems[i];
aElement.setAttribute('href', '#');
liElement.appendChild(aElement);
tocUlElement.appendChild(liElement);
}
let menuDiv = document.getElementById('menu');
for(let i = 0; i < menu.length; i++){
let section = document.createElement('div');
section.classList.add('section');
let sectionHeader = document.createElement('div'); // move this here
sectionHeader.classList.add('section-header');
section.appendChild(sectionHeader);
let sectionEntries = document.createElement('div'); // create container for the entries
sectionEntries.classList.add('section-entries', 'hidden'); // initially hide
section.appendChild(sectionEntries);
for(let j = 0; j < menu[i].length; j++) {
if(j == 0) {
sectionHeader.classList.add(menu[i][0].toLowerCase());
sectionHeader.textContent = menu[i][j];
}
else{
let entry = document.createElement('div');
entry.classList.add('entry');
entry.classList.add(menu[i][0].toLowerCase());
let nameOfFood = document.createElement('div');
let description = document.createElement('div');
let price = document.createElement('div');
nameOfFood.classList.add('name-of-food');
description.classList.add('description');
price.classList.add('price');
nameOfFood.textContent = menu[i][j][0];
description.textContent = menu[i][j][1];
price.textContent = menu[i][j][2];
entry.appendChild(nameOfFood); //
entry.appendChild(description);
entry.appendChild(price);
sectionEntries.appendChild(entry); // Append the entry to the section entries container
}
menuDiv.appendChild(section);
}
}
// Just define one click handler on the whole menu
menuDiv.addEventListener('click', function(e){
// If click was on a header, then toggle the display of the entries container element below it
if (e.target.classList.contains("section-header")) {
e.target.nextElementSibling.classList.toggle("hidden"); 
}
});
/* Function definitions */
function generateTOCItemsAsArray(menu){
let tocItemsAsArray = []
for(i = 0; i < menu.length; i++){
tocItemsAsArray.push(menu[i][0]);
}
return tocItemsAsArray;
}
.section-header { font-weight: bold; margin-top: 10px  }
.entry { margin-left: 10px }
.name-of-food { font-style: italic; margin-top: 5px }
.hidden { display: none }
<li id="toc-list"></li>
<div id="menu"></div>

最后,我应该补充一点,有很多库可以为此提供漂亮的小部件,比如手风琴、折叠/展开。。。以及其他影响。如果你再次经历从头开始创造这些东西的所有麻烦,那将是一种遗憾。

相关内容

  • 没有找到相关文章

最新更新