element.remove() seem to weird.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script>
class View {
constructor() {
this.parent = document.querySelector('#root')
}
template() {
return `
<div>Random Num</div>
<div>${Math.random()}</div>
<button type='button'>Reset Random Num</button>
`
}
render() {
const templateElement = document.createElement('template')
templateElement.innerHTML = this.template()
templateElement.content
.querySelector('button')
.addEventListener('click', () => {
this.rerender()
})
this.parent.append(templateElement.content)
}
rerender() {
this.parent.childNodes.forEach((item) => {
// here is the problem
item.remove()
})
// const templateElement = document.createElement('template')
// templateElement.innerHTML = this.template()
// this.parent.append(templateElement.content)
}
}
const viewIns = new View()
viewIns.render()
</script>
</html>
when I click the button, doms don't get removed at all,and then I clicked the button again ,only the div contains the random number left,I'm really confused why is that.
CodePudding user response:
childNodes
returns a live collection. Live collections can be unintuitive to work with, because they can mutate themselves while you're iterating over them and throw you off. Here, you're .remove()
ing a text node, and then the <div>Random Num</div>
instantly becomes the 0th index in the collection. Then you move on to the 1st index in the collection, which is another text node. The process repeats a few times, removing all text nodes, but not removing any elements.
Turn the childNodes into an array first, so that the collection won't change while you're iterating over it.
[...this.parent.childNodes].forEach((item) => {
class View {
constructor() {
this.parent = document.querySelector('#root')
}
template() {
return `
<div>Random Num</div>
<div>${Math.random()}</div>
<button type='button'>Reset Random Num</button>
`
}
render() {
const templateElement = document.createElement('template')
templateElement.innerHTML = this.template()
templateElement.content
.querySelector('button')
.addEventListener('click', () => {
this.rerender()
})
this.parent.append(templateElement.content)
}
rerender() {
[...this.parent.childNodes].forEach((item) => {
item.remove()
})
// const templateElement = document.createElement('template')
// templateElement.innerHTML = this.template()
// this.parent.append(templateElement.content)
}
}
const viewIns = new View()
viewIns.render()
<div id="root"></div>
Or, an easier approach would be to just set the .innerHTML
of the parent to the empty string.
this.parent.innerHTML = '';