Home > Enterprise >  Why does getting a data-attribute within a loop work, but still throws an error?
Why does getting a data-attribute within a loop work, but still throws an error?

Time:11-05

I have some input elements and I want to create a reset button, so that I can programatically revert their values to their original state.

Here is my code:

let people = document.getElementsByClassName('person')

function reset() {
  for (var key in people) {
    let string = people[key].dataset.name
    people[key].value = string
  }
}

let app = document.getElementById('app')
let button = document.createElement('button')
button.innerHTML = "reset"
button.addEventListener('click', reset)
app.append(button)
<div id="app">
  <input class="person" data-name="John" value="John">
  <input class="person" data-name="Steve" value="Steve">
  <input class="person" data-name="Peter" value="Peter">
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

This actually works as intended, but I get an error saying that the value of 'data-name' cannot be read, but this cannot be true as the function works as intended.

Does anyone know what is happening here and how I can fix this?

CodePudding user response:

in iterates over all enumerable properties in not only the object itself, but also in all objects in the internal prototype chain. Log the key, and you'll see:

let people = document.getElementsByClassName('person')

function reset() {
  for (var key in people) {
    console.log(key);
  }
}
reset();
<div id="app">
  <input class="person" data-name="John" value="John">
  <input class="person" data-name="Steve" value="Steve">
  <input class="person" data-name="Peter" value="Peter">
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

The HTMLCollection has more enumerable properties than just 0, 1, and 2, so trying to access the .dataset on those other properties fails.

Use for..of instead, to invoke the collection's iterator (which will only iterate over the elements in the collection, as desired).

let people = document.getElementsByClassName('person')

function reset() {
  for (const input of people) {
    input.value = input.dataset.name;
  }
}
let app = document.getElementById('app')
let button = document.createElement('button')
button.innerHTML = "reset"
button.addEventListener('click', reset)
app.append(button)
<div id="app">
  <input class="person" data-name="John" value="John">
  <input class="person" data-name="Steve" value="Steve">
  <input class="person" data-name="Peter" value="Peter">
</div>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

In general, I'd suggest avoiding in loops usually - often, for..of or Object.keys or Object.entries work better.

CodePudding user response:

This line of code here

for (var key in people) 

should be

for (let i = 0; i < people.length; i  )

as the for...in... loop will also get the length property of the people HTMLCollection. You would then be accessing the value property of people.length, which does not exist.

Completed Code:

let people = document.getElementsByClassName('person')

function reset() {
for (let i = 0; i < people.length; i  ) {
  let string = people[i].dataset.name
  people[i].value = string
}
}

let app = document.getElementById('app')
let button = document.createElement('button')
    button.innerHTML = "reset"
    button.addEventListener('click',reset)
    app.append(button)
<div id="app">
<input class="person" data-name="John" value="John">
<input class="person" data-name="Steve" value="Steve">
<input class="person" data-name="Peter" value="Peter">
</div>
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related