2

I have a problem with the creation and deletion of my tags, let me explain.

I created a Tag class which is instantiated when submitting a form, this same class dynamically inserts its HTML into the DOM using the displayTag() method

So far, everything is working. But the problem I am having is when I decide to create multiple tags, I can indeed create several but when I want to click on the delete tag button, only the last tag inserted in the DOM works, and the buttons coming from the other tags are no longer associated with events declared in the bindEvents() method of my Tag class.

I'm not sure if this is very clear, but you can look at the code below, create multiple tags and try to remove them all to observe the problem.

The code I provided is a bit different and very simplified from my project, but the error remains the same!

Thank you for taking the time to read me and for all of your help!

class Tag {
  constructor(name) {
    this.el = null
    this.name = name
    
    this.init()
  }
  
  init = async () => {
    this.el = await this.displayTag()
    
    this.bindEvents()
  }
  
  bindEvents = () => {
    const removeButton = this.el.querySelector('button')
    
    removeButton.addEventListener('click', this.removeTag)
  }
  
  displayTag = () => {
    return new Promise(resolve => {
      const wrapper = document.querySelector('.tags__wrapper')
      
      let tagEl = `<div class="tag" data-name="${this.name}"><div class="tag__wrapper">`
      tagEl += `<b class="tag__name">${this.name}</b>`
      tagEl += `<button>Delete</button>`
      tagEl += `</div></div>`
      
      wrapper.innerHTML += tagEl
      
      resolve(document.querySelector(`[data-name=${this.name}]`))
    })
  }
  
  removeTag = (e) => {
    e.preventDefault()
    
    this.el.remove()
    delete this
  }

}

class Page {
  constructor() {
    this.tags = []
    
    this.init()
  }
  
  init = () => {
    this.bindEvents()
  }
  
  bindEvents = () => {
    const button = document.querySelector("button")
    
    button.addEventListener("click", this.createTag)
  }
  
  createTag = (e) => {
    e.preventDefault()
  
    const name = document.querySelector("input").value
    const tag = new Tag(name)
    
    this.tags.push(tag)
  }
}

new Page()
<h1>Hello, add tags</h1>
<form>
  <label for="tag">Tag :</label>
  <input type="text" id="tag" name="tag" />
  <button>Add Tag</button>
</form>
<hr />
<h3>Tags</h3>
<div class="tags__wrapper">
</div>
tomzidani
  • 21
  • 1

1 Answers1

1

If you create multiple tags with the same value there will be a few elements with the same data-name value and document.querySelector will always only get the first one, not the newest one. Instead I'd recommend using document.createElement so you can be sure that you always have the right one:

const wrapper = document.querySelector('.tags__wrapper')
      
const tagEl = document.createElement('div')
tagEl.className = 'tag'
tagEl.dataset.name = this.name
tagEl.innerHTML = `<div class="tag__wrapper">
    <b class="tag__name">${this.name}</b>
    <button>Delete</button>
</div>`
      
wrapper.appendChild(tagEl)
     
resolve(tagEl)

jsfiddle

Why my version works while yours doesn't is that when you modify innerHTML it removes all listeners that existed previously. More about this

lusc
  • 1,206
  • 1
  • 10
  • 19