Строптивый и не отзывчивый
  • Есть список в нем его детишки-liшки:

    ul {
        padding: 30px;
        border: 1px solid #8f93b6;
    }
    
    li {
        list-style-type: none;
        margin-bottom: 20px;
        border: 1px dashed black;
    }
    <ul id="elem">
        <li id="li">text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
    </ul>

    И вот я задумал обратится к <li> и по клику именно на него добавлять вопросительный знак в конец его текста, а в случае клика на <ul> добавить новый элемент <li> снизу. Не сработало, хотя я писал самые вежливы обращения:

    let ul = document.querySelector('#elem')
    
    //Ниже варианты обращения к li
    let li = ul.firstElementChild.cloneNode(true)
    let liID = document.getElementById('li')
    let liAll = document.querySelectorAll('li')
    // let liTag = document.getElementsByTagName('li') // HTMLcollection тоже самое что и liAll
    // let liCh = ul.children // HTMLcollection тоже самое что и liAll
    
    
    
    ul.addEventListener('click', foo)
    
    function foo(ev) {
      console.log(ev.target) // <li>
      console.log("Put all <li> together version: ", ev.target === liAll );         // false при обращении к HTMLcollection
      console.log("Personal index version: ",        ev.target === liAll[0]);      // true только если кликнуть на  указанный индекс
      console.log("Personal <li id='li'> version: ", ev.target === liID);         // true только если кликнуть на указанный ID
    
      if (ev.target === li) { // Тут было вместо li - 'li', liAll, liID, liAll[0], 'LI'
        ev.target.innerHTML += '?'
      }
      this.append(li)
    }
    

    Выходит, чтобы добраться до <li> мне нужно каждому придать id и привязать событие к нему, что абсурд. Или перебрать циклом HTMLcollection(не важно каким способом добытую) и связать элементы цикла с событием ev, что тоже кажется не изящным.

    Одно интересное, что в ev.target есть свойства nodeName: "LI"; и localName: "li". А значит ev.target.localName === 'li'; или ev.target.nodeName === 'LI'; вернет true и наша задумка заработает, но нет тут уникальности, что если на странице много и других li под другими ul, а мне нужны именно эти li.

    Вопрос как добраться до всех <li> без цикла, без id, и фамильярных обращений в стиле ev.target.localName? Второе почему у меня добавляется в конец <ul> всего один элемент <li>? Потом событие будто отвязывается. Хотелось бы множить их нажатиями.


    Ответы (3 шт):

    Автор решения: rusgeli

    Скорее всего кроме как через цикл или получение свойств события не получится никак. Вы, на самом деле почти добились поставленной цели, только вам нужно сравнивать не ev.target с li, а ev.target.nodeName с 'li', и тогда ? будет добавляться к нажатому элементу. Поскольку событие вы навесили на родительский ul, другие ul и li затронуты никак не будут (кроме вложенных, это исправляется доп проверкой ev.target.parentElement == ul).

    Почему добавляется только один элемент? Потому что вы пытаетесь добавить один и тот же элемент много раз (это как дважды на елку повесить один и тот же шарик). Вам необходимо создавать или клонировать элемент, а потом уже добавлять в конец.

    Вот примерный вариант:

        let ul = document.querySelector('#elem')
    
        //Ниже варианты обращения к li
        let li = ul.firstElementChild.cloneNode(true)
        let liID = document.getElementById('li')
        let liAll = document.querySelectorAll('li')
        // let liTag = document.getElementsByTagName('li') // HTMLcollection тоже самое что и liAll
        // let liCh = ul.children // HTMLcollection тоже самое что и liAll
    
    
    
        ul.addEventListener('click', foo)
    
        function foo(ev) {
          console.log(ev.target) // <li>
          console.log("Put all <li> together version: ", ev.target === liAll );         // false при обращении к HTMLcollection
          console.log("Personal index version: ",        ev.target === liAll[0]);      // true только если кликнуть на  указанный индекс
          console.log("Personal <li id='li'> version: ", ev.target === liID);         // true только если кликнуть на указанный ID
    
          if (ev.target.nodeName === 'LI' && ev.target.parentElement == ul) { 
            ev.target.innerHTML += '?'
          }
          let li1 = document.createElement('li'); 
          li1.innerHTML = 'text'
          this.append(li1)
        }
    ul {
        padding: 30px;
        border: 1px solid #8f93b6;
    }
    
    li {
        list-style-type: none;
        margin-bottom: 20px;
        border: 1px dashed black;
    }
    <ul id="elem">
        <li id="li">text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
    </ul>

    → Ссылка
    Автор решения: webDev_

    const ul = document.querySelector('#elem')
    
    function foo(ev) {
      if (ev.target.tagName === 'LI') {
        return ev.target.innerHTML += '?'
      }
      const li = ul.firstElementChild.cloneNode(true)
      this.append(li)
    }
    
    ul.addEventListener('click', foo)
    ul {
        padding: 30px;
        border: 1px solid #8f93b6;
    }
    
    li {
        list-style-type: none;
        margin-bottom: 20px;
        border: 1px dashed black;
    }
    <ul id="elem">
        <li id="li">text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
    </ul>

    → Ссылка
    Автор решения: Rudi

    Уникальный id для ul и потом смотреть, являются ли li дочерними. Почему один добавляется уже написали в других ответах.. Так же другие ответы работают не корректно у @rusgeli при клике на li добавляется элемент.. У @webDev_ если кликнули по первому li, затем клонируется и вставляется с уже добавленным ?.. Без фамильярности вряд ли.. Всё таки вы работаете с вложенными элементами. Вопрос только в том, какой путь идентификации li выбрать..

    let ul = document.querySelector('#elem');
    
    ul.addEventListener('click', function(e){    
        let elem = e.target;
        let parentId = elem.parentNode.getAttribute('id');
        if (parentId == "elem") {
          elem.innerHTML += "?"; 
        } else if (elem.getAttribute('id') == "elem") {
          let li = document.createElement('li');
          li.innerHTML = 'text';
          this.append(li);
        }
    });
    ul {
        padding: 30px;
        border: 1px solid #8f93b6;
    }
    
    li {
        list-style-type: none;
        margin-bottom: 20px;
        border: 1px dashed black;
    }
    <ul id="elem">
        <li>text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
        <li>text</li>
    </ul>

    → Ссылка