charAt 无法处理在 javascript 中动态创建的元素

charAt not working on element created dynamically in javascript

我正在 javascript 中制作名称搜索应用程序。我在 javascript.

中动态创建了所有列表项

Js Fiddle

当我使用 charAt(0) 时,应用程序无法运行,没有任何显示 我想匹配输入的第一个字符和数组的第一个字符

names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"]

let ulEl = document.getElementById("ul-el")
let searchInput = document.getElementById("searchInput");

function render(things) {
  let listItems = ""
  for (let i = 0; i < things.length; i++) {
    listItems += `
            <li class="names">
                    ${things[i]}
            </li>
        `
  }
  ulEl.innerHTML = listItems
}

render(names)

searchInput.addEventListener("keyup", function(event) {
  let searchQuery = event.target.value.toLowerCase().charAt(0);
  let allNamesDOMCollection = document.getElementsByClassName('names');

  console.log(allNamesDOMCollection.textContent)


  console.log(allNamesDOMCollection.textContent)
  for (let i = 0; i < allNamesDOMCollection.length; i++) {
    let currentName = allNamesDOMCollection[i].textContent.toLowerCase().charAt(0);


    if (currentName.includes(searchQuery)) {
      allNamesDOMCollection[i].style.display = "block";
    } else {
      allNamesDOMCollection[i].style.display = "none";
    }

  }
})
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>

undefined 是因为 getElementsByClassName 返回的 HTMLCollection 没有 textContent 属性,但因为那只是在你的 console.log 我们基本上可以忽略这一点。

主要问题是列表项的第一个字符是空白字符,因为你如何构建 listItems 字符串,如果你想要第一个,你需要 trim() 文本内容名称的字符。 (或更改构建方式 listItems。)

我还建议使用 input 事件,而不是 keyup

你的搜索输入允许多个字符,但你只使用第一个字符,这似乎很奇怪,但你在评论中确认这就是你想要的,所以我就这样了——但请看结尾如果您愿意,请回答如何进行完全匹配。

查看 *** 条评论:

// *** Add a declaration
const names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];

let ulEl = document.getElementById("ul-el");
let searchInput = document.getElementById("searchInput");

function render(things) {
    let listItems = "";
    for (let i = 0; i < things.length; i++) {
        listItems += `
            <li class="names">
                    ${things[i]}
            </li>
        `;
    }
    ulEl.innerHTML = listItems;
}

render(names);

// *** Use `input`, not `keyup`
searchInput.addEventListener("input", function(event) {
    let searchQuery = event.target.value.toLowerCase().charAt(0);
    let allNamesDOMCollection = document.getElementsByClassName('names');

    for (let i = 0; i < allNamesDOMCollection.length; i++) {
        // *** Remove the whitespace before getting first character
        let currentName = allNamesDOMCollection[i].textContent.trim().toLowerCase().charAt(0);
        // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^
        if (currentName.includes(searchQuery)) {
            allNamesDOMCollection[i].style.display = "block";
        } else {
            allNamesDOMCollection[i].style.display = "none";
        }
    }
});
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>

或者,更改构建方式 listItems:

function render(things) {
    let listItems = "";
    for (let i = 0; i < things.length; i++) {
        listItems += `<li class="names">${things[i]}</li>`; // *** Remove whitespace around name
    }
    ulEl.innerHTML = listItems;
}

Live Copy:

// *** Add a declaration
const names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];

let ulEl = document.getElementById("ul-el");
let searchInput = document.getElementById("searchInput");

function render(things) {
    let listItems = ""
    for (let i = 0; i < things.length; i++) {
        listItems += `<li class="names">${things[i]}</li>`; // *** Remove whitespace around name
    }
    ulEl.innerHTML = listItems;
}

render(names);

// *** Use `input`, not `keyup`
searchInput.addEventListener("input", function(event) {
    let searchQuery = event.target.value.toLowerCase().charAt(0);
    let allNamesDOMCollection = document.getElementsByClassName('names');

    for (let i = 0; i < allNamesDOMCollection.length; i++) {
        let currentName = allNamesDOMCollection[i].textContent.toLowerCase().charAt(0);
        if (currentName.includes(searchQuery)) {
            allNamesDOMCollection[i].style.display = "block";
        } else {
            allNamesDOMCollection[i].style.display = "none";
        }
    }
});
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>


一些其他的旁注。在处理自然语言文本时,一般来说最好是:

  • 优先选择 toLocaleLowerCase()(和 toLocaleUpperCase())而非特定于语言环境的版本。

  • 使用normalize() (in environments where it's supported) to normalize any sequences of combining characters, etc., so you aren't hit by false negatives because the same glyph (ç, for instance) is written in different ways (ç [the "c-cedilla" code point, U+00E7] and [a c followed by a cedilla/cédille combining mark (U+0327],例如,它们看起来几乎相同但不是相同的代码点序列)。例如,如果您不进行规范化,并且用户使用单个代码点输入 Franç(例如),则如果用 c 和组合标记:

let input = `Franç`;
let name = "François";
console.log(name.includes(input)); // false
input = input.normalize();
name = name.normalize();
console.log(name.includes(input)); // true


在评论中,您询问如果您确实想要根据整个名称检查整个输入,您会更改什么。为此,如果要匹配名称中的任何位置,只需删除两个 .charAt(0) 调用即可。如果只想匹配名称的开头,请使用 startsWith 而不是 includes.

这是上面的,使用:

  • includes 匹配名称中的任意位置
  • toLocaleLowerCasenormalize
  • for-of (ES2015+) 而不是 for 因为它更方便
  • a class 而不是内联样式来隐藏名称

const names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];

const ulEl = document.getElementById("ul-el");
const searchInput = document.getElementById("searchInput");

function render(things) {
    let listItems = "";
    for (const thing of things) {
        listItems += `
            <li class="names">
                    ${thing}
            </li>
        `;
    }
    ulEl.innerHTML = listItems;
}

render(names);

function prepForCompare(str) {
    str = str.trim().toLocaleLowerCase();
    // `normalize` is newer than most of the rest of what we're using
    if (str.normalize) {
        str = str.normalize();
    }
    return str;
}

searchInput.addEventListener("input", function(event) {
    const searchQuery = prepForCompare(event.target.value);
    const allNamesDOMCollection = document.getElementsByClassName("names");
    for (const li of allNamesDOMCollection) {
        const currentName = prepForCompare(li.textContent);
        li.classList.toggle("hidden", !currentName.includes(searchQuery));
    }
});
.hidden {
    display: none;
}
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>

我用 forEach 替换了 for 循环,因为你试图做的是对每个元素应用相同的代码行(几乎是 forEach 所做的)。

您的代码不适用于多个字符,因为您只比较第一个字符。删除两个 charAt(0),它应该可以正常工作。

names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"]

let ulEl = document.getElementById("ul-el")
let searchInput = document.getElementById("searchInput");

function render(things) {
  let listItems = ""
  for (let i = 0; i < things.length; i++) {
    listItems += `<li class="names">${things[i]}</li>`
  }
  ulEl.innerHTML = listItems;
}

render(names)

searchInput.addEventListener("keyup", function(event) {
  let searchQuery = event.target.value.toLowerCase();
  let allNamesDOMCollection = document.querySelectorAll('.names');


  allNamesDOMCollection.forEach(e => {
    let currentName = e.innerText.toLowerCase();
    if (searchQuery == "") {
      e.style.display = "block";
    } else if (currentName.includes(searchQuery)) {
      e.style.display = "block";
    } else {
      e.style.display = "none";
    }

  })
})
body {
  margin: 0;
  background: #A1FFCE;
  /* fallback for old browsers */
  background: -webkit-linear-gradient(to right, #FAFFD1, #A1FFCE);
  /* Chrome 10-25, Safari 5.1-6 */
  background: linear-gradient(to right, #FAFFD1, #A1FFCE);
  /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
  font-family: 'Roboto', sans-serif;
}

h1 {
  color: #363635;
  text-align: center;
  letter-spacing: 2px;
  font-size: 3rem;
}

.container {
  width: 600px;
  margin: 0 auto;
}

.cont-modifier {
  height: 550px;
  overflow: auto;
}

input[type=text] {
  width: 100%;
  box-sizing: border-box;
  border: 2px solid #595A4A;
  border-radius: 4px;
  font-size: 16px;
  background-color: white;
  padding: 12px 20px 12px 40px;
  background-image: url('search-line.png');
  background-position: 10px 10px;
  background-repeat: no-repeat;
}

ul {
  margin: 0;
  padding: 0;
}

.names {
  list-style: none;
  border-bottom: 3px solid #0b1354;
  margin: 3px;
  padding: 20px;
  font-size: 20px;
  font-weight: 300;
  letter-spacing: 2px;
}
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>


如果你想 'strict' 检查(例如:目前,输入 'A' 也会显示 'Kaira'、'Bilal' 等),你可以在其中添加另一个条件将检查第一个字符是否匹配。

names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"]

let ulEl = document.getElementById("ul-el")
let searchInput = document.getElementById("searchInput");

function render(things) {
  let listItems = ""
  for (let i = 0; i < things.length; i++) {
    listItems += `<li class="names">${things[i]}</li>`
  }
  ulEl.innerHTML = listItems;
}

render(names)

searchInput.addEventListener("input", function(event) {
  let searchQuery = event.target.value.toLowerCase();
  let allNamesDOMCollection = document.querySelectorAll('.names');


  allNamesDOMCollection.forEach(e => {
    let currentName = e.innerText.toLowerCase();

    if (searchQuery == "") {
      e.style.display = "block";
    } else if (currentName.includes(searchQuery) && currentName[0] === searchQuery[0]) {
      e.style.display = "block";
    } else {
      e.style.display = "none";
    }

  })
})
body {
  margin: 0;
  background: #A1FFCE;
  /* fallback for old browsers */
  background: -webkit-linear-gradient(to right, #FAFFD1, #A1FFCE);
  /* Chrome 10-25, Safari 5.1-6 */
  background: linear-gradient(to right, #FAFFD1, #A1FFCE);
  /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
  font-family: 'Roboto', sans-serif;
}

h1 {
  color: #363635;
  text-align: center;
  letter-spacing: 2px;
  font-size: 3rem;
}

.container {
  width: 600px;
  margin: 0 auto;
}

.cont-modifier {
  height: 550px;
  overflow: auto;
}

input[type=text] {
  width: 100%;
  box-sizing: border-box;
  border: 2px solid #595A4A;
  border-radius: 4px;
  font-size: 16px;
  background-color: white;
  padding: 12px 20px 12px 40px;
  background-image: url('search-line.png');
  background-position: 10px 10px;
  background-repeat: no-repeat;
}

ul {
  margin: 0;
  padding: 0;
}

.names {
  list-style: none;
  border-bottom: 3px solid #0b1354;
  margin: 3px;
  padding: 20px;
  font-size: 20px;
  font-weight: 300;
  letter-spacing: 2px;
}
<h1>Search Names</h1>

<div class="container">
  <input type="text" id="searchInput" placeholder="Search">
</div>

<div class="container cont-modifier">

  <ul id="ul-el"></ul>

</div>