Как запретить повторное выполнение Function, когда конкретный ITEM уже был нажат?

const filter = () => {
  const menu = document.querySelector('.portfolio__menu'),
    items = menu.querySelectorAll('li'),
    wrapper = document.querySelector('.portfolio__blocks'),
    markAll = wrapper.querySelectorAll('.all'),
    no = document.querySelector('.portfolio-no');

  const typeFilter = (markType) => {
    let cName = wrapper.getElementsByClassName(markType);

    markAll.forEach(mark => {
      mark.style.display = 'none';
      mark.classList.remove('animated', 'fadeIn');
    });

    no.style.display = 'none';
    no.classList.remove('animated', 'fadeIn');

    if (cName.length >= 1) {
      [...cName].forEach(mark => {
        mark.style.display = 'block';
        mark.classList.add('animated', 'fadeIn');
      });

    } else {
      no.style.display = 'block';
      no.classList.add('animated', 'fadeIn');
    }
  };

  items.forEach(item => {
    item.addEventListener('click', () => {
      typeFilter(item.className);
    });
  });


  menu.addEventListener('click', (e) => {
    let target = e.target;

    if (target && target.tagName == "LI") {
      items.forEach(btn => btn.classList.remove('active'));
      target.classList.add('active');
    }
  });
};

filter();
.portfolio__menu {
  display: flex;
  flex-wrap: wrap;
  justify-content: left;
  margin-top: 40px;
  margin-bottom: 30px;
  border-top: 2px solid white;
}

li {
  margin-bottom: 20px;
  display: inline-block;
  font-family: 'Arial', sans-serif;
  list-style-type: none;
  font-size: 19px;
  line-height: 21px;
  padding: 15px 20px;
  cursor: pointer;
  color: #fff;
  box-shadow: inset 0 3px 0 0 #F5D6AB;
  background-color: rgba(128, 128, 128, 0.4);
  transition: all .3s ease-in;
}

li.active,
li:hover {
  box-shadow: inset 0 3px 0 0 #C66C49;
  background-color: #F5D6AB;
}

.portfolio__blocks {
  max-width: 1200px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: 1fr;
  grid-gap: 10px;
}

.portfolio__block {
  width: 1fr;
  height: 100px;
  background: grey;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-size: 25px;
}

.portfolio-no {
  display: none;
}

.animated {
  animation-duration: 1s;
  animation-fill-mode: both;
}

.fadeIn {
  animation-name: fadeIn;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
<div class="portfolio__heading">
  <ul class="portfolio__menu">
    <li class="all active">All</li>
    <li class="latest">Latest</li>
    <li class="ux-design">UX-Design</li>
    <li class="front-end">Front-end</li>
    <li class="ui-design">UI-Design</li>
  </ul>
</div>
<div class="portfolio__blocks">
  <div class="portfolio__block all latest">
    1
  </div>
  <div class="portfolio__block all latest">
    2
  </div>
  <div class="portfolio__block all latest">
    3
  </div>
  <div class="portfolio__block all ux-design">
    4
  </div>
  <div class="portfolio__block all ux-design">
    5
  </div>
  <div class="portfolio__block all ui-design">
    6
  </div>
  <div class="portfolio__block all ui-design">
    7
  </div>
  <div class="portfolio__block all front-end">
    8
  </div>
  <div class="portfolio__block all front-end">
    9
  </div>
  <div class="portfolio__block all front-end">
    10
  </div>
</div>
<div class="portfolio-no">
  <p>I have not done such work yet</p>
</div>

Проблема в том что если ещё раз кликнуть активный пункт меню то появится: I have not done such work yet - сообщение о том, что таких элементов нет. Как это исправить? Должно работать как сейчас, но если нажать активный пункт меню еще раз все должно оставаться, то-есть нажал пункт Latest - подтянулись блоки с классом Latest, Нажал еще раз Latest - блоки остались на прежнем месте. Пробовал через event.stopPropagation() - не помогло(если я все правильно сделал). Всем Спасибо!


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

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

Если не ошибаюсь, проблема в этой строчке:

typeFilter(item.className);

Когда вы вторично кликаете на пункте меню, в className находится не только название типа вроде latest, но и маркер активности, то есть latest active. Соответственно, let cName = wrapper.getElementsByClassName(markType); ничего не находит, так как у вас нет div-блоков с классом вроде latest active, а есть только latest.

Возможные выходы:

  • Разделить маркировку активности и тип задания. Например, передавать тип через другой атрибут вроде data- атрибутов — см. HTMLElement.dataset. Наверное, это сложнее добавить, но так будет логичнее и надёжнее по сравнению с другими способами.

  • Ещё в обработчике щелчка на меню проверять наличие класса active в списке классов li, и если он есть, не вызывать typeFilter(item.className);. Наверное, самый экономный способ, хоть он и оставит смешение семантики и представления. Можно добавить к предыдущему способу, это решит обе проблемы)

  • Убирать маркировку активности из аргумента, например:

    typeFilter(item.className.replace(' active', ''));
    

    Это проще добавить, но останется смешение семантики и представления, и это будет больше похоже на костыль, на мой вкус)

→ Ссылка