英文:
getElementsByTagName returns undefined even with the script before /body
问题
我是一个完全不懂JavaScript的初学者,我完全被卡住了。我尝试通过调整在教程中找到的代码来创建一个搜索栏过滤系统。目标是能够按电影的名称、导演或发行年份进行过滤。
每个电影的信息分别在<h1>
、<h2>
和<h3>
标签之间。排序是通过getElementsByTagName
完成的,并且对导演和发行年份正常工作,但只要我添加电影标题,就什么都不起作用了。
从我在Firefox中看到的情况来看,导致问题的原因是我存储电影名称的变量未定义(Uncaught TypeError: a is undefined)。经过一些研究,我了解到此错误在页面加载前运行脚本时出现,因此我将其放置在</body>
之前,但问题仍然存在。
我想理解我的代码有什么问题。
function myFunction() {
// 声明变量
var input, filter, ul, li, a, b, c, i, title, director, date;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// 遍历所有列表项,并隐藏不匹配搜索查询的项
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("h1")[0];
b = li[i].getElementsByTagName("h2")[0];
c = li[i].getElementsByTagName("h3")[0];
title = a.textContent || a.innerText;
director = b.textContent || b.innerText;
date = c.textContent || c.innerText;
if (title.toUpperCase().indexOf(filter) > -1 || director.toUpperCase().indexOf(filter) > -1 || date.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
<ul id="myUL" class="gallery container">
<li>
<div class="overlay"></div>
<div class="info">
<a href="#" class="btn"><img src="example.jpg"></a>
<div class="description">
<h1 style="display:none">TITLE</h1>
<h2>DIRECTOR</h2>
<h3>DATE</h3>
<p>
SYNOPSIS
</p>
</div>
</div>
<div class="bg-img">
<img src="example2.jpg">
</div>
</li>
</ul>
希望这可以帮助你找到代码中的问题。
英文:
I am a complete beginner in JavaScript and I am completely blocked. I am trying to create a search bar filtering system by adapting a code found in a tutorial. The goal is to be able to filter a list of movies by name, director or year of release.
Each of these information are respectively between tags <h1>
, <h2>
then <h3>
. The sorting is done by getElementsByTagName
and works as expected with the director and year of release, but as soon as I add the title of the movies, nothing works anymore.
From what I see in Firefox, the cause being that the variable in which I store the movie name is not defined (Uncaught TypeError: a is undefined). After some research, I was able to read that this error appears when the script is run before the page is loaded, so I placed it just before the /body, but the problem persists.
I would like to understand what is wrong with my code.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function myFunction() {
// Declare variables
var input, filter, ul, li, a, b, c, i, title, director, date;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("h1")[0];
b = li[i].getElementsByTagName("h2")[0];
c = li[i].getElementsByTagName("h3")[0];
title = a.textContent || a.innerText;
director = b.textContent || b.innerText;
date = c.textContent || c.innerText;
if (title.toUpperCase().indexOf(filter) > -1 || director.toUpperCase().indexOf(filter) > -1 || date.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
<!-- language: lang-html -->
<ul id="myUL" class="gallery container">
<li>
<div class="overlay"></div>
<div class="info">
<a href="#" class="btn"><img src="example.jpg"></a>
<div class="description">
<h1 style="display:none">TITLE</h1>
<h2>DIRECTOR</h2>
<h3>DATE</h3>
<p>
SYNOPSIS
</p>
</div>
</div>
<div class="bg-img">
<img src="example2.jpg">
</div>
</li>
</ul>
<!-- end snippet -->
答案1
得分: 1
以下是您要翻译的内容:
在原帖中,<script>
标签位于正确位置。不幸的是,这只是解决方案的一部分。
问题
-
HTML中的
<input>
在哪里? -
为每个类别过滤器添加了复选框:
title
、director
和date
。 -
使用过时的方法,如
.getElementsByTagName()
在[某些情况下][1]可能会有问题。相反,使用[.querySelectorAll()
][2]。 -
在
window
的“load”事件上调用搜索函数几乎没有意义。页面与用户之间的交互可以在<input id="find">
或<form>
上触发的“click”、“input”、“change”或“submit”事件上处理。在示例中,一个<form>
包裹了所有元素,并注册了[“submit”事件][3]。 -
.textContent
和.innerText
几乎是[相同的][4],使用其中之一即可。 -
<h1>
到<h6>
是标题,应用于按重要性排序的内容的标题。页面应只使用一个<h1>
。将<h1>
、<h2>
和<h3>
添加到每个<li>
不仅令人讨厌,而且在语义上也很糟糕。使用CSS来调整font-size
和font-weight
。
示例中有注释的详细信息
注意:源代码的某些部分带有仅供演示目的的标签,这不是必需的,只是用来生成<ul>
的内容。
// 仅供演示目的
const data = [{
"title": "Plain Dirty (a.k.a. Briar Patch)",
"director": "Jodi MacEllen",
"date": "7/16/2018"
}, {
"title": "Cake",
"director": "Faber Pude",
"date": "5/3/2012"
}, {
"title": "Exit Smiling",
"director": "Bryant Whytock",
"date": "1/26/1972"
}, {
"title": "Mission: Impossible III",
"director": "Allison Kayzer",
"date": "5/14/1986"
}, {
"title": "Hollywood Between Paranoia and Sci-Fi. The Power of Myth",
"director": "Isacco Yoell",
"date": "3/14/1980"
}, {
"title": "Barabbas",
"director": "Humfrid Scandrett",
"date": "6/12/2018"
}, {
"title": "Stolen (Stolen Lives)",
"director": "Harv Ginman",
"date": "2/19/2014"
}, {
"title": "Casper",
"director": "Ferne Nester",
"date": "9/20/2011"
}, {
"title": "Journey to the Beginning of Time",
"director": "Dodi Chaster",
"date": "12/4/2014"
}, {
"title": "Home Run",
"director": "Nonna Bugler",
"date": "5/4/1972"
}];
// 引用<ul>
const list = document.querySelector("ul");
// 仅供演示目的
data.forEach(movie => {
const li = `<li><i>${movie.title}</i> -
<b>${movie.director}</b> -
<time>${movie.date}</time></li>`;
list.insertAdjacentHTML("beforeend", li);
});
// 引用<form>
const F = document.forms.main;
// 引用所有<input>和<fieldset>
const fc = F.elements;
// 引用<input id="find">
const find = fc.find;
// 将所有[name="chx"]的HTMLCollection转换为数组
const filters = Array.from(fc.chx);
// 将所有<li>转换为NodeList,然后转换为数组
const items = Array.from(document.querySelectorAll("li"));
// 注册<form>到“submit”事件
F.onsubmit = searchList;
// 事件处理程序传递事件对象
function searchList(event) {
// 阻止<form>重定向页面
event.preventDefault();
/**
* 从<input id="find">获取用户搜索词
* 将字符串转换为数组。
*/
const keywords = find.value.toLowerCase().split(" ");
// 隐藏所有<li>
items.forEach(li => li.style.display = "none");
// 获取每个已选复选框的索引号数组
const checked = filters.flatMap((chx, idx) => chx.checked ? idx : []);
/**
* 对于每个<li>...
* ...从<li>中的<i>、<b>和<time>中获取文本的数组...
* ...创建一个仅包含与已检查数组中的数字匹配的索引的文本的数组,然后将其转换为字符串...
* ...如果搜索词中的一个也在当前<li>的文本中...
* ...显示<li>
*/
items.forEach(li => {
let text = Array.from(li.children).map(ele => ele.textContent.toLowerCase());
let filtered = text.filter((txt, cnt) => checked.includes(cnt)).join("");
if (keywords.some(word => filtered.includes(word))) {
li.style.display = "list-item";
}
});
}
:root {
font: 2ch/1.15 "Segoe UI";
}
fieldset {
padding-right: 20px;
}
input {
font: inherit;
height: 3.5ex;
}
#find {
width: 80%;
margin: 15px 0 0 25px;
}
[type="submit"] {
font-variant: small-caps;
cursor: pointer;
}
menu,
label {
display: flex;
align-items: center;
margin-left: 15px;
}
menu
<details>
<summary>英文:</summary>
In the OP, the `<script>` tag is in the correct place. Unfortunately, that was only a part of the solution.
Problems
-
- Where's the `<input>` in the HTML?
- Added a checkbox for each category filter: `title`, `director`, and `date`.
- Using antiquated methods such as `.getElementsByTagName()` can be problematic under [certain circumstances][1]. Instead, use [`.querySelectorAll()`][2].
- Calling the search function on "load" event of `window` makes very little sense. The interaction between the page and user can be handled on a "click", "input", "change", or "submit" event triggered on either the `<input id="find">` or a `<form>`. In the example, a `<form>` is wrapped around all of the elements and is registered to the ["submit" event][3].
- `.textContent` and `.innerText` are [almost identical][4], use one or the other.
- `<h1>` thru `<h6>` are headings and should be used for titles of content organized in the order of importance. Only a single `<h1>` should be used for a page. Adding `<h1>`, `<h2>`, and `<h3>` to each `<li>` is not only an eyesore but it's semantically horrible as well. Use CSS to adjust `font-size` and `font-weight` instead.
**Details are commented in example**
**Note:** There are parts of the source labeled with *For demo purposes*, which is not required and is there just to generate the content for the `<ul>`.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
// For demo purposes
const data = [{
"title": "Plain Dirty (a.k.a. Briar Patch)",
"director": "Jodi MacEllen",
"date": "7/16/2018"
}, {
"title": "Cake",
"director": "Faber Pude",
"date": "5/3/2012"
}, {
"title": "Exit Smiling",
"director": "Bryant Whytock",
"date": "1/26/1972"
}, {
"title": "Mission: Impossible III",
"director": "Allison Kayzer",
"date": "5/14/1986"
}, {
"title": "Hollywood Between Paranoia and Sci-Fi. The Power of Myth",
"director": "Isacco Yoell",
"date": "3/14/1980"
}, {
"title": "Barabbas",
"director": "Humfrid Scandrett",
"date": "6/12/2018"
}, {
"title": "Stolen (Stolen Lives)",
"director": "Harv Ginman",
"date": "2/19/2014"
}, {
"title": "Casper",
"director": "Ferne Nester",
"date": "9/20/2011"
}, {
"title": "Journey to the Beginning of Time",
"director": "Dodi Chaster",
"date": "12/4/2014"
}, {
"title": "Home Run",
"director": "Nonna Bugler",
"date": "5/4/1972"
}];
// Reference <ul>
const list = document.querySelector("ul");
// For demo purposes
data.forEach(movie => {
const li = `<li><i>${movie.title}</i> -
<b>${movie.director}</b> -
<time>${movie.date}</time></li>`;
list.insertAdjacentHTML("beforeend", li);
});
// Reference the <form>
const F = document.forms.main;
// Reference all <input> and <fieldset>
const fc = F.elements;
// Reference <input id="find">
const find = fc.find;
// Make HTMLCollection of all [name="chx"] into an array
const filters = Array.from(fc.chx);
// Make NodeList of all <li> then into an array
const items = Array.from(document.querySelectorAll("li"));
// Register <form> to "submit" event
F.onsubmit = searchList;
// Event handler passes Event Object
function searchList(event) {
// Stop <form> from redirecting page
event.preventDefault();
/**
* Get user search words from <input id="find">
* Convert string into an array.
*/
const keywords = find.value.toLowerCase().split(" ");
// Hide all <li>
items.forEach(li => li.style.display = "none");
// Get an array of index numbers of each checked checkbox
const checked = filters.flatMap((chx, idx) => chx.checked ? idx : []);
/**
* For each <li>...
* ...Make an array (text) of text from <i>, <b>, and <time> in <li>...
* ...Make an array (filtered) of texts of only index that
* match the numbers in checked array and convert it into a string...
* ...If one of the search words is also in the text of the current <li>...
* ...show the <li>
*/
items.forEach(li => {
let text = Array.from(li.children).map(ele => ele.textContent.toLowerCase());
let filtered = text.filter((txt, cnt) => checked.includes(cnt)).join("");
if (keywords.some(word => filtered.includes(word))) {
li.style.display = "list-item";
}
});
}
<!-- language: lang-css -->
:root {
font: 2ch/1.15 "Segoe UI";
}
fieldset {
padding-right: 20px;
}
input {
font: inherit;
height: 3.5ex;
}
#find {
width: 80%;
margin: 15px 0 0 25px;
}
[type="submit"] {
font-variant: small-caps;
cursor: pointer;
}
menu,
label {
display: flex;
align-items: center;
margin-left: 15px;
}
menu {
list-style: none;
margin-left: -15px;
}
<!-- language: lang-html -->
<form id="main">
<fieldset>
<legend>Search by keywords delimited by a space</legend>
<input id="find" placeholder="x the 2018" type="search"><input type="submit" value="Find">
<menu>Filters:&nbsp;
<label><input name="chx" type="checkbox" checked> Title </label>
<label><input name="chx" type="checkbox" checked> Director </label>
<label><input name="chx" type="checkbox" checked> Date </label>
</menu>
<ul></ul>
</fieldset>
</form>
<!-- end snippet -->
[1]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection
[2]: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
[3]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event
[4]: https://www.microfocus.com/documentation/silk-test/200/en/silktestworkbench-help-en/SILKTEST-21EEFF3F-DIFFERENCEBETWEENTEXTCONTENTSINNERTEXTINNERHTML-REF.html
</details>
# 答案2
**得分**: 0
你可以在代码中添加 `window.onload` 来调用你的函数,这意味着只有在页面加载完成后才会触发你的函数。
```javascript
window.onload = function() {
myFunction();
};
function myFunction() {
// 声明变量
var input, filter, ul, li, a, b, c, i, title, director, date;
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// 循环遍历所有列表项,并隐藏不匹配搜索查询的项
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("h1")[0];
b = li[i].getElementsByTagName("h2")[0];
c = li[i].getElementsByTagName("h3")[0];
title = a.textContent || a.innerText;
director = b.textContent || b.innerText;
date = c.textContent || c.innerText;
console.log(title, director, date);
if (title.toUpperCase().indexOf(filter) > -1 || director.toUpperCase().indexOf(filter) > -1 || date.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
<ul id="myUL" class="gallery container">
<li>
<div class="overlay"></div>
<div class="info">
<a href="#" class="btn"><img src="https://picsum.photos/200"></a>
<div class="description">
<h1 style="display:none">TITLE</h1>
<h2>DIRECTOR</h2>
<h3>DATE</h3>
<p>
SYNOPSIS
</p>
</div>
</div>
<div class="bg-img">
<img src="https://picsum.photos/200">
</div>
</li>
</ul>
这是你提供的代码的翻译部分。
英文:
you could add windoe.onload to call your function, meaning only when page is loaded your function will fire.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
window.onload = function() {
myFunction();
};
function myFunction() {
// Declare variables
var input, filter, ul, li, a, b, c, i, title, director, date;
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("h1")[0];
b = li[i].getElementsByTagName("h2")[0];
c = li[i].getElementsByTagName("h3")[0];
title = a.textContent || a.innerText;
director = b.textContent || b.innerText;
date = c.textContent || c.innerText;
console.log(title, director, date);
if (title.toUpperCase().indexOf(filter) > -1 || director.toUpperCase().indexOf(filter) > -1 || date.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
<!-- language: lang-html -->
<ul id="myUL" class="gallery container">
<li>
<div class="overlay"></div>
<div class="info">
<a href="#" class="btn"><img src="https://picsum.photos/200"></a>
<div class="description">
<h1 style="display:none">TITLE</h1>
<h2>DIRECTOR</h2>
<h3>DATE</h3>
<p>
SYNOPSIS
</p>
</div>
</div>
<div class="bg-img">
<img src="https://picsum.photos/200">
</div>
</li>
</ul>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论