Как правильно переключать классы с помощью document.getElementsByClassName и Element.classList

У меня есть страница, на которой я хочу иметь возможность включать или выключать классы для определенных элементов.

В попытке сделать это я написал функцию:

function toggleClass(obj) {
    alert(obj.className);
      Array.from(document.getElementsByClassName(obj.className)).forEach(function(element) {
        element.classList.toggle(obj.className);
    });
}

который вызывается элементами в таблице, например <td style=""><span class="determiner" onclick="toggleClass(this)">Determiner</span></td>.

Это отлично работает, когда я впервые щелкаю элемент, срабатывает предупреждение и класс удаляется. Когда я снова щелкаю тот же элемент в таблице, чтобы попытаться переключить/добавить класс, срабатывает предупреждение (пустое, потому что класс был удален), но класс не добавляется.

Может ли кто-нибудь посоветовать, что я делаю неправильно здесь?

function toggleClass(obj) {
    alert(obj.className);
      Array.from(document.getElementsByClassName(obj.className)).forEach(function(element) {
        element.classList.toggle(obj.className);
    });
}
.determiner {
  /* color: rgb(248, 8, 8); */
  border: 1px solid rgb(248, 8, 8);
}

.preposition {
  color: rgb(40, 18, 236);
  /* border: 1px solid rgb(40, 18, 236); */
}

.verb-present {
  color: rgb(13, 146, 68);
  /* border: 3px solid rgb(13, 146, 68); */
}

span[class^='noun-'], span[class*=' noun-']{
  color: #F00;
}
<table class="grammar table table-hover" data-toggle="table" data-sort-name="instance_use" data-sort-order="desc">
            <thead><tr><th style="" data-field="grammar_type" tabindex="0"><div class="th-inner sortable both">Grammar Type</div><div class="fht-cell"></div></th><th style="" data-field="instance_use" tabindex="0"><div class="th-inner sortable both desc">Instances of Use</div><div class="fht-cell"></div></th></tr></thead>
            <tbody><tr data-index="0"><td style=""><span class="adverb" onclick="toggleClass(this)">Adverb</span></td><td style="">2 </td></tr><tr data-index="1"><td style=""><span class="verb-present" onclick="toggleClass(this)">Verb, present</span></td><td style="">2 </td></tr><tr data-index="2"><td style=""><span class="determiner" onclick="toggleClass(this)">Determiner</span></td><td style="">2 </td></tr><tr data-index="3"><td style=""><span class="noun-sing-or-mass" onclick="toggleClass(this)">Noun, sing. or mass</span></td><td style="">1 </td></tr><tr data-index="4"><td style=""><span class="" onclick="toggleClass(this)">Preposition</span></td><td style="">1 </td></tr><tr data-index="5"><td style=""><span class="noun-plural" onclick="toggleClass(this)">Noun, plural</span></td><td style="">1 </td></tr><tr data-index="6"><td style=""><span class="comma" onclick="toggleClass(this)">Comma</span></td><td style="">1 </td></tr><tr data-index="7"><td style=""><span class="personal-pronoun" onclick="toggleClass(this)">Personal pronoun</span></td><td style="">1 </td></tr></tbody>
        </table>
        
<div id="story_text">
            <span style="white-space: pre-line">
            <span class="adverb">here</span> <span class="verb-present">is</span> <span class="determiner">a</span> <span class="noun-sing-or-mass">story</span> <span class="">with</span> <span class="determiner">no</span> <span class="noun-plural">commas</span><span class="comma">,</span> <span class="adverb">now</span> <span class="personal-pronoun">it</span> <span class="verb-present">does</span>
            </span>
        </div>


person Stuart Brown    schedule 23.06.2018    source источник


Ответы (1)


Проблема в том, что вы продолжаете ссылаться на obj.className, но в цикле forEach после повторения выбранного элемента obj.className становится пустым, поэтому при дальнейших итерациях (например, "a" в is a story) строка, переданная element.classList.toggle, пуста. .

Другая проблема заключается в том, что когда вы пытаетесь снова щелкнуть элемент, у него нет существующего класса, поэтому obj.className пусто — нет класса для переключения обратно.

Вместо того, чтобы добавлять обработчики в HTML-атрибуты (что так же плохо, как eval), попробуйте выполнить итерацию по всем элементам, которым требуется обработчик заранее, чтобы вы могли создать массив элементов для каждого className, который вы хотите иметь. переключать.

Менее элегантным решением (которое потребовало бы намного меньше изменений) было бы переключение другого класса, который переопределяет свойства color и border determiner и других классов:

function toggleClass(obj) {
  const className = obj.classList[0];
  document.querySelectorAll('.' + className).forEach(element => {
    element.classList.toggle('blank');
  });
}
.determiner {
  /* color: rgb(248, 8, 8); */
  border: 1px solid rgb(248, 8, 8);
}

.preposition {
  color: rgb(40, 18, 236);
  /* border: 1px solid rgb(40, 18, 236); */
}

.verb-present {
  color: rgb(13, 146, 68);
  /* border: 3px solid rgb(13, 146, 68); */
}

span[class^='noun-'],
span[class*=' noun-'] {
  color: #F00;
}

.blank {
  border: 0;
  color: black !important;
}
<table class="grammar table table-hover" data-toggle="table" data-sort-name="instance_use" data-sort-order="desc">
            <thead><tr><th style="" data-field="grammar_type" tabindex="0"><div class="th-inner sortable both">Grammar Type</div><div class="fht-cell"></div></th><th style="" data-field="instance_use" tabindex="0"><div class="th-inner sortable both desc">Instances of Use</div><div class="fht-cell"></div></th></tr></thead>
            <tbody><tr data-index="0"><td style=""><span class="adverb" onclick="toggleClass(this)">Adverb</span></td><td style="">2 </td></tr><tr data-index="1"><td style=""><span class="verb-present" onclick="toggleClass(this)">Verb, present</span></td><td style="">2 </td></tr><tr data-index="2"><td style=""><span class="determiner" onclick="toggleClass(this)">Determiner</span></td><td style="">2 </td></tr><tr data-index="3"><td style=""><span class="noun-sing-or-mass" onclick="toggleClass(this)">Noun, sing. or mass</span></td><td style="">1 </td></tr><tr data-index="4"><td style=""><span class="" onclick="toggleClass(this)">Preposition</span></td><td style="">1 </td></tr><tr data-index="5"><td style=""><span class="noun-plural" onclick="toggleClass(this)">Noun, plural</span></td><td style="">1 </td></tr><tr data-index="6"><td style=""><span class="comma" onclick="toggleClass(this)">Comma</span></td><td style="">1 </td></tr><tr data-index="7"><td style=""><span class="personal-pronoun" onclick="toggleClass(this)">Personal pronoun</span></td><td style="">1 </td></tr></tbody>
        </table>
        
<div id="story_text">
            <span style="white-space: pre-line">
            <span class="adverb">here</span> <span class="verb-present">is</span> <span class="determiner">a</span> <span class="noun-sing-or-mass">story</span> <span class="">with</span> <span class="determiner">no</span> <span class="noun-plural">commas</span><span class="comma">,</span> <span class="adverb">now</span> <span class="personal-pronoun">it</span> <span class="verb-present">does</span>
            </span>
        </div>

person CertainPerformance    schedule 23.06.2018
comment
Это потрясающе, спасибо @CertainPerformance. Я думаю, что в это время ночи (для меня) я пока пойду по менее элегантному маршруту и ​​завтра утром посмотрю по-новому :) - person Stuart Brown; 24.06.2018
comment
Вместо добавления обработчиков в атрибуты HTML (что так же плохо, как и eval) несколько преувеличено. Встроенные слушатели не более похожи на использование eval, чем на размещение скрипта в элементе скрипта. - person RobG; 24.06.2018