Home > OS >  Click event only works on the second click
Click event only works on the second click

Time:12-23

i hope you guys fine, well..

I'm doing a To Do List, and there is a problem in my code, which I've been trying to solve for a few days, and no effective results was made..

If you guys test in the snippet with me, i am sure, that will be more clear to understand.

When i click in some list element, my javascript should change or add the className, and add a class call 'selected'.

because, when i will click in the remove button, they will delete all elements with 'selected' classList in the list. (as you can see in the code)

But the className a not being add to the tag

  • in the first click, just works if i click in the
  • element one more time.

    i simplified my code, just to show the real problem:

    Link to jsfiddle : https://jsfiddle.net/myqrzcs2/

    const textoTarefa = document.getElementById('texto-tarefa');
    const criarTarefa = document.getElementById('criar-tarefa');
    const listaTarefas = document.getElementById('lista-tarefas');
    criarTarefa.onclick = function click() {
      const lista = document.createElement('li');
      lista.className = 'lista';
      lista.id = 'lista';
      lista.tabIndex = '0';
      lista.innerHTML = textoTarefa.value;
      listaTarefas.appendChild(lista);
      document.body.appendChild(listaTarefas);
      textoTarefa.value = '';
    };
    const completedLine = document.querySelector('ol');
    
    function umClick(event) {
      if (event.target.tagName === 'LI') {
        const listas = document.querySelectorAll('.lista');
        listas.forEach((i) => {
          i.addEventListener('click', function semNomeDois() {
            listas.forEach((j) => j.classList.remove('selected'));
            this.classList.add('selected');
          });
        });
      }
    }
    completedLine.addEventListener('click', umClick);
    
    function removeSelected() {
      // teste
      const listaSelected = document.querySelectorAll('.selected');
      for (let i = 0; i < listaSelected.length; i  = 1) {
        listaSelected[i].remove();
      }
    }
    .lista:focus {
      background: red;
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <link rel='stylesheet' href='style.css'>
    </head>
    
    <body>
      <header>
        <h1>My List</h1>
      </header>
      <input id='texto-tarefa' type="text" />
      <button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
      <ol id='lista-tarefas'>
      </ol>
      <button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
      <script src="script.js"></script>
    </body>
    
    </html>

    But how can i make the class be add, just in the first click, not in the second?


  • CodePudding user response:

    You were unnecessarily adding an event listener to each item in the list.

    You can check the updated fiddle here: https://jsfiddle.net/msa9v2nf/

    Since you're already checking which target element is clicked, there isn't any need to add an individual listener to each child item in the list.

    I updated the umClick function:

    function umClick(event) {
      if (event.target.tagName === 'LI') {
        const listas = document.querySelectorAll('.lista');
        
        listas.forEach((i) => {
          listas.forEach((j) => j.classList.remove('selected'));
          event.target.classList.add('selected');
        });
      }
    }
    

    CodePudding user response:

    I think you got off on the wrong foot in programming this. Here is the way I use, may it inspire you.

    const
      textoTarefa  = document.getElementById('texto-tarefa')
    , criarTarefa  = document.getElementById('criar-tarefa')
    , removerSelec = document.getElementById('remover-selecionado')
    , listaTarefas = document.getElementById('lista-tarefas')
      ;
    var li_selected = null
      ;
    textoTarefa.oninput = () =>
      {
      criarTarefa.disabled = (textoTarefa.value.trim().length ===0 )
      }
    criarTarefa.onclick = () =>
      {
      listaTarefas.appendChild( document.createElement('li')).textContent =  textoTarefa.value.trim()
      textoTarefa.value = ''
      textoTarefa.focus()
      criarTarefa.disabled = true
      }
    listaTarefas.onclick = ({target}) =>
      {
      if (!target.matches('li')) return
      if (!!li_selected && li_selected !== target ) li_selected.classList.remove('listaSelect')
      li_selected = target.classList.toggle('listaSelect') ? target : null
      removerSelec.disabled = !li_selected
      }
    removerSelec.onclick = () =>
      {
      listaTarefas.removeChild(li_selected)
      li_selected = null
      removerSelec.disabled = true
      }
    .listaSelect {
      background: #ff0000c4;
      }
    ol#lista-tarefas {
      cursor : pointer
      }
    <input  id='texto-tarefa' type="text" value="">
    <button id='criar-tarefa' disabled>Add</button>
    <button id='remover-selecionado' disabled>Remove Selected</button>
    <ol id='lista-tarefas'></ol>

    CodePudding user response:

    The problem is you call the function umClick and call the function to add .selected within a click event in the same function umClick.

    What happens is the click event completedLine.addEventListener('click', umClick); happens before the i.addEventListener('click', function semNomeDois() event. This is why you need a first click on the ol tag for only the first time.

    To fixes this you have multiple options:

    1. instead of calling click event on ol tag you can call mousedown which happens before click event.
    2. Calling a click event on the li elements on creation, which needs a new function.
    3. Depending on Vektor's answer, you can remove the unnecessary click event inside the first click event.

    Also, I've made the red highlight on the .selected class instead of :focus, just to make it clear when the item is selected.

    .selected {
       background: red;
    }
    

    First Solution

    const textoTarefa = document.getElementById('texto-tarefa');
    const criarTarefa = document.getElementById('criar-tarefa');
    const listaTarefas = document.getElementById('lista-tarefas');
    criarTarefa.onclick = function click() {
      const lista = document.createElement('li');
      lista.className = 'lista';
      lista.id = 'lista';
      lista.tabIndex = '0';
      lista.innerHTML = textoTarefa.value;
      listaTarefas.appendChild(lista);
      document.body.appendChild(listaTarefas);
      textoTarefa.value = '';
    };
    const completedLine = document.querySelector('ol');
    
    function umClick(event) {
      if (event.target.tagName === 'LI') {
        const listas = document.querySelectorAll('.lista');
        listas.forEach((i) => {
          i.addEventListener('click', function semNomeDois() {
            listas.forEach((j) =>{
              if(j != event.target)
                j.classList.remove('selected');
            });
            this.classList.add('selected');
          });
        });
      }
    }
    completedLine.addEventListener('mousedown', umClick);
    
    function removeSelected() {
      // teste
      const listaSelected = document.querySelectorAll('.selected');
      for (let i = 0; i < listaSelected.length; i  = 1) {
        listaSelected[i].remove();
      }
    }
    .selected {
      background: red;
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <link rel='stylesheet' href='style.css'>
    </head>
    
    <body>
      <header>
        <h1>My List</h1>
      </header>
      <input id='texto-tarefa' type="text" />
      <button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
      <ol id='lista-tarefas'>
      </ol>
      <button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
      <script src="script.js"></script>
    </body>
    
    </html>

    Second Solution

    const textoTarefa = document.getElementById('texto-tarefa');
    const criarTarefa = document.getElementById('criar-tarefa');
    const listaTarefas = document.getElementById('lista-tarefas');
    criarTarefa.onclick = function click() {
      const lista = document.createElement('li');
      lista.className = 'lista';
      lista.id = 'lista';
      lista.tabIndex = '0';
      lista.innerHTML = textoTarefa.value;
      listaTarefas.appendChild(lista);
      lista.addEventListener('click',function(){
        itemClick(this);
      });
      document.body.appendChild(listaTarefas);
      textoTarefa.value = '';
    };
    
    function itemClick(item) {
        const listas = document.querySelectorAll('.lista');
        listas.forEach((j) =>j.classList.remove('selected'));
       item.classList.add('selected');
    }
    
    function removeSelected() {
      // teste
      const listaSelected = document.querySelectorAll('.selected');
      for (let i = 0; i < listaSelected.length; i  = 1) {
        listaSelected[i].remove();
      }
    }
    .selected {
      background: red;
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <link rel='stylesheet' href='style.css'>
    </head>
    
    <body>
      <header>
        <h1>My List</h1>
      </header>
      <input id='texto-tarefa' type="text" />
      <button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
      <ol id='lista-tarefas'>
      </ol>
      <button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
      <script src="script.js"></script>
    </body>
    
    </html>

    CodePudding user response:

    I am not fully understand your problem but, If you want to add the style when selecting a item, just add the style to

    .selected
    

    If you want in focus, and remove the class when there is no focus, you may add an eventlistener to control that.

    • Related