Блокирование третьей кнопки при активных двух

Рейтинг: 0Ответов: 2Опубликовано: 31.01.2023

Всем привет, использую vanilla JS, есть три кнопки категорий, максимум может быть нажато две кнопки, если две кнопки нажато на третью нажать нельзя. Могут быть нажаты любые 2 кнопки из трёх, при этом третья должна блокироваться (disabled).

const categoriesButtons = document.querySelectorAll('.service__button');

categoriesButtons.forEach((button) => {
    button.addEventListener('click', (event) => {
        if (event.target.id === 'gardens') {
            button.classList.toggle('button--active');
        }
        else if (event.target.id === 'lawn') {
            button.classList.toggle('button--active');  

        } else if (event.target.id === 'planting') {
            button.classList.toggle('button--active');
        }
        
        // if (event.target.id === 'gardens' && event.target.id === 'lawn') {
        //     plantingBtn.classList.add('.button--disabled')
        // }  => Этот код не работает
    })
})

Ответы

▲ 0

При нажатых 2х кнопках, оставшейся будет установлен нужный класс. Если кнопку отжать, класс удаляется

const categoriesButtons = document.querySelectorAll('.service__button');
const selectButtons = []; // Нажатые кнопки
const allButtons = []; // Все кнопки
let disabledButton = ''; // С признаком disable

categoriesButtons.forEach((button) => {
  allButtons.push(button.id); // Записываем в справочник все найденные
  button.addEventListener('click', (event) => {
    // Добавляем или удаляем кнопку
    if (selectButtons.includes(event.target.id)) {
      selectButtons.splice(selectButtons.indexOf(event.target.id), 1)
    } else {
      selectButtons.push(event.target.id)
    }

    if (event.target.id === 'gardens') {
      button.classList.toggle('button--active');
    }
    else if (event.target.id === 'lawn') {
      button.classList.toggle('button--active');

    } else if (event.target.id === 'planting') {
      button.classList.toggle('button--active');
    }
    // Проверяем, если нажато 2, то оставшейся добавляем стиль
    if (selectButtons.length === 2) {
      disabledButton = allButtons.filter(it => !selectButtons.includes(it))[0];
      document.getElementById(disabledButton).classList.add('button--disabled');
    } else if (disabledButton) { // Иначе стиль удаляем
      document.getElementById(disabledButton)?.classList.remove('button--disabled');
      disabledButton = '';
    }
  })
})
.service__button {
  background-color: #DDDDDD;
}

.button--active {
  background-color: #11AA11;
}

.button--disabled {
  background-color: #FF5555;
  pointer-events: none;
}
<button class="service__button" id="gardens">тык раз</button>
<button class="service__button" id="lawn">тык два</button>
<button class="service__button" id="planting">тык три</button>

▲ 0

Евгений, для поведения кнопок, которое Вы описали в вопросе, лучше подошли бы чекбоксы. Да, в разметке придется написать немного больше кода, но это будет правильный подход.

// За всю отрисовку состояния инпутов отвечает одна функция, опираясь на данные из состояния.
const render = (state, controls) => {
  const activeCount = Object.values(state).filter(v => v === 'checked').length // Здесь получаем количество 'чекнутых' инпутов

  if (activeCount === state.maxActiveCount) { // Никаких магических чисел.
    controls.forEach(input =>
      state[input.dataset.service] === 'checked' // input.dataset.service - достает значение из data атрибута в инпуте
        ? input.setAttribute("checked", "")
        : input.setAttribute("disabled", "")
    )
  } else {
    controls.forEach(input =>
      state[input.dataset.service] === 'checked'
        ? input.setAttribute("checked", "")
        : input.removeAttribute("disabled")
    )
  }
}

/* Здесь используем замыкание функции для доступа к состоянию. Реализуем сосотяние опираясь на количество инпутов. В реальных приложениях лучше использовать proxy или библиотеку для подписки на изменения состояния */
const createHandler = (controls = []) => {
  const state = controls.reduce((acc, input) => {
    const value = input.dataset.service
    return {...acc, [value]: 'idle'}
  },{maxActiveCount: 3}) // Как обсуждалось в комментариях добавим переменную которая регулирует количество активных кнопок

  return ({target}) => { // Это обработчик событий
    const value = target.dataset.service
    state[value] = state[value] === 'idle' ? 'checked' : 'idle'
    render(state, controls)
  }
}

// Здесь находим форму и делигируем события инпутов на один обработчик событий
const service = document.getElementById('service')
const serviceControls = Array.from(service.elements)
const handleChange = createHandler(serviceControls)
service.addEventListener("change", handleChange)
.service {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 20px;
  padding: 10px;
  background-color: slateblue;
}

.control input { /*прячем инпут*/
  appearance: none;
}

.control__button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  padding: 8px 16px;
  border-radius: 10px;
  text-wrap: none;
  background-color: #DDDDDD;
  cursor: pointer;
}

.service__control input:checked + .control__button {
  background-color: #11AA11;
}

.service__control input:disabled + .control__button {
  background-color: #FF5555;
}
<form class='service' id="service">
    <label class="control service__control">
        <input type="checkbox" data-service="gardens" />
        <span class="control__button">тык раз</span>
    </label>
    <label class="control service__control">
        <input type="checkbox" data-service="lawn" />
        <span class="control__button"> тык два</span>
    </label>
    <label class="control service__control">
        <input type="checkbox" data-service="planting" />
        <span class="control__button">тык три</span>
    </label>
    <label class="control service__control">
        <input type="checkbox" data-service="fourth-button" />
        <span class="control__button">тык четыре</span>
    </label>
    <label class="control service__control">
        <input type="checkbox" data-service="fifth-button" />
        <span class="control__button">тык пять</span>
    </label>
</form>

Теперь, это маштабируемый код, можете хоть 10 кнопок обработать и если Вам нужно куда-то отправлять данные с выбранных инпутов, Вам всего лишь останется добавить кнопку для отправки в разметку формы, повесить листенер на форму(которая уже есть) и написать обработчик на событие submit и все готово!)