【问题标题】:charAt not working on element created dynamically in javascriptcharAt 不适用于在 javascript 中动态创建的元素
【发布时间】:2021-08-27 11:45:03
【问题描述】:

我正在用 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>

【问题讨论】:

  • 嗨!请使用现场 Stack Snippets 功能而不是非现场资源。 Here's how to do one.
  • getElementsByClassName 返回一个数组,你需要循环遍历它的值,console.log(allNamesDOMCollection[0].textContent) 例如工作
  • @AlexandreElshobokshy - 它返回一个HTMLCollection,而不是一个数组。在他们的实际代码中,他们正在循环,只是他们没有console.log
  • 是的,我的意思是它不是一个固定的定义值。他在问为什么它返回未定义,我只是解释了:)
  • 为什么你只得到列表中每个名字的第一个字符?您的搜索字段允许多个...

标签: javascript html charat


【解决方案1】:

undefined 的事情是因为 getElementsByClassName 返回的 HTMLCollection 没有 textContent 属性,但由于它只是在您的 console.log 中,我们可以在很大程度上忽略它。

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

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

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

***cmets:

// *** 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;
}

实时复制:

// *** 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()(在支持它的环境中)对任何组合字符序列等进行规范化,因此您不会被误报打击,因为相同的字形(例如ç)以不同的方式编写方式(例如,ç [“c-cedilla”代码点,U+00E7] 和 [a c 后跟一个 cedilla/cédille 组合标记(U+0327],看起来几乎相同)但不是相同的代码点序列)。例如,如果您不进行规范化,并且用户使用单个代码点键入Franç(例如),它将不会在列表中找到François如果它是用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,因为它更方便一点
  • 一个类而不是内联样式来隐藏名称

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>

【讨论】:

  • 非常感谢。它现在正在工作。你能告诉我如何匹配多个字符吗?我只允许匹配第一个字符是我的错误
  • @coder - 只需删除两个 .charAt(0) 调用,如果您想匹配名称中的任何位置,您就可以开始了。如果您只想匹配名称的开头,请使用startsWith 而不是includes。但是请参阅我添加到答案末尾的注释。
  • @coder - 不客气!很高兴有帮助。我在对多个字符串进行匹配并合并其他注释的末尾添加了一个示例。
【解决方案2】:

我将 for 循环替换为 forEach,因为您尝试对每个元素应用相同的代码行(几乎与 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>

如果您想要“严格”检查(例如:当前,输入“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>

【讨论】:

  • @coder 根据您的尝试,我认为您应该看看我回答的第二部分。
猜你喜欢
  • 1970-01-01
  • 2012-07-02
  • 2011-02-24
  • 2015-12-18
  • 1970-01-01
  • 1970-01-01
  • 2013-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多