Как не писать пять раз один и тот же код только с разными классами?

С помощью js я оформила выпадающий список вместо select. Но если я буду использовать 5 списков, то мне придется 5 раз js код писать только с разными классами.

Я думаю, это плохая практика.

Подскажите пожалуйста как можно сделать единую функцию для всех выпадающих списков?

function ready() {
    const select = document.querySelector('.select');
    const select_title = select.querySelector('.select_title');
    const select_labels = select.querySelectorAll('.select_label');

    select_title.addEventListener('click', () => {
        if ('active' === select.getAttribute('data-state')) {
            select.setAttribute('data-state', '');
        } else {
            select.setAttribute('data-state', 'active');
        }
    });

    // Close when click to option
    for (let i = 0; i < select_labels.length; i++) {
        select_labels[i].addEventListener('click', (evt) => {
            select_title.textContent = evt.target.textContent;
            select.setAttribute('data-state', '');
        });
    }
    
    ///close outside the element
    $(document).mouseup(function (e) {
        select.setAttribute('data-state', '');
    });
}
    document.addEventListener("DOMContentLoaded", ready);
.select {
    position: relative;
    width: 300px;
    height: 40px;
    margin: 20px 0;
}

.select[data-state="active"] .select_title::before {
    transform: translate(-3px, -50%) rotate(-45deg);
}

.select[data-state="active"] .select_title::after {
    transform: translate(3px, -50%) rotate(45deg);
}

.select[data-state="active"] .select_content {
    opacity: 1;
}

.select[data-state="active"] .select_label + .select_input + .select_label {
    max-height: 40px;
    border-top-width: 1px;
}

.select_title {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    padding: 8px 16px;
    border-radius: 5px;
    border: solid 1px #c7ccd1;
    cursor: pointer;
    background-color: #38b0ff;
    color: #e9e9e9;
}

.select_title::before, .select_title::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 16px;
    display: block;
    width: 10px;
    height: 2px;
    transition: all 0.3s ease-out;
    background-color: #e9e9e9;
    transform: translate(-3px, -50%) rotate(45deg);
}

.select_title::after {
    transform: translate(3px, -50%) rotate(-45deg);
}

.select_title:hover {
   color: #ffffff;
}

.select_title:hover::before, .select_title:hover::after {
    background-color: #f3fdff;
}

.select_content {
    position: absolute;
    top: 40px;
    left: 3px;
    display: flex;
    flex-direction: column;
    width: calc(100% - 6px);
    background-color: #ffffff;
    border: 1px solid #c7ccd1;
    border-top: none;
    border-bottom-left-radius: 5px;
    border-bottom-right-radius: 5px;
    transition: all 0.3s ease-out;
    opacity: 0;
    z-index: 8;
}

.select_input {
    display: none;
}

.select_input:checked + label {
    background-color: #dedede;
}

.select_input:disabled + label {
    opacity: 0.6;
    pointer-events: none;
}

.select_label {
    display: flex;
    align-items: center;
    width: 100%;
    height: 40px;
    max-height: 0;
    padding: 0 20px;
    transition: all 0.1s ease-out;
    cursor: pointer;
    overflow: hidden;

}

.select_label + input + .select_label {
    border-top: 0 solid #C7CCD160;
}

.select_label:hover {
    background-color: #38b0ff !important;
    color: #ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="select" data-state="">
    <div class="select_title type_request" value="" data-default="">Выберите тип</div>
    <div class="select_content">
        <input id="option0" class="select_input" value="" type="radio" name="singleSelect" />
        <label for="option0" class="select_label">Выберите тип</label>
        <input id="option1" class="select_input type_request" value="1" type="radio" name="singleSelect" />
        <label for="option1" class="select_label">Один</label>
        <input id="option2" class="select_input type_request" value="2" type="radio" name="singleSelect" />
        <label for="option2" class="select_label">Два</label>
        <input id="option3" class="select_input type_request" value="3" type="radio" name="singleSelect" />
        <label for="option3" class="select_label">Три</label>
        <input id="option4" class="select_input type_request" value="4" type="radio" name="singleSelect" />
        <label for="option4" class="select_label">Четыре</label>
    </div>
</div>


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

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

Как вариант - на лету рендерить html.

function createElement(items) {
  let elem = document.createElement("div");
  elem.classList.add('select');
  elem.dataset.state = "";
  addToggleEvent(elem);
 
  let title = document.createElement("div");
  title.classList.add('select_title','type_request');
  title.dataset.default = "";
  title.innerHTML = items[0].title;
 
  elem.append(title);

  let content = document.createElement("div");
  content.classList.add('select_content');

  items.forEach(function(item, idx) {
    let input = createInputItem(idx),
      label = createLabelItem(idx, item);
    content.append(input);
    content.append(label);
    addLabelEvent(label, title, elem);
  });
  
  elem.append(content);

  document.querySelector('body').append(elem);

  function createInputItem(idx) {
    let input = document.createElement("input");
    input.id = "option" + idx;
    input.classList.add('select_input');
    if (idx > 0) {
      input.classList.add('type_request');
    }
    input.value = idx == 0 ? "" : idx;
    input.type = "radio";
    input.name = "singleSelect";
    return input;
  }

  function createLabelItem(idx, item) {
    let label = document.createElement("label");
    label.for = "option" + idx;
    label.classList.add('select_label');
    label.innerHTML = item.title;
    return label;
  }
  
  function addToggleEvent(title){
    elem.addEventListener('click', e => {
  		elem.dataset.state = elem.dataset.state === 'active' ? '' : 'active';
  	});
  }
  
  function addLabelEvent(label, title, elem){
    label.addEventListener('click', e => {
      e.stopPropagation();
      title.textContent = e.target.textContent;
      elem.dataset.state = "";
    });
  }
  
  $(document).mouseup(function (e) {
    elem.dataset.state = "";
  });
}

createElement([
  { title: "1-5" },
  { title: "Один" },
  { title: "Два" },
  { title: "Три" },
  { title: "Четыре" },
  { title: "Пять" }
]);

createElement([
  { title: "6-10" },
  { title: "Шесть" },
  { title: "Сумь" },
  { title: "Восемь" },
  { title: "Девять" },
  { title: "Десять" }
]);

createElement([
  { title: "Да/Нет?" },
  { title: "Нет" },
  { title: "Да" }
]);
.select {
    position: relative;
    width: 300px;
    height: 40px;
    margin: 20px 0;
}

.select[data-state="active"] .select_title::before {
    transform: translate(-3px, -50%) rotate(-45deg);
}

.select[data-state="active"] .select_title::after {
    transform: translate(3px, -50%) rotate(45deg);
}

.select[data-state="active"] .select_content {
    opacity: 1;
}

.select[data-state="active"] .select_label + .select_input + .select_label {
    max-height: 40px;
    border-top-width: 1px;
}

.select_title {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    padding: 8px 16px;
    border-radius: 5px;
    border: solid 1px #c7ccd1;
    cursor: pointer;
    background-color: #38b0ff;
    color: #e9e9e9;
}

.select_title::before, .select_title::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 16px;
    display: block;
    width: 10px;
    height: 2px;
    transition: all 0.3s ease-out;
    background-color: #e9e9e9;
    transform: translate(-3px, -50%) rotate(45deg);
}

.select_title::after {
    transform: translate(3px, -50%) rotate(-45deg);
}

.select_title:hover {
   color: #ffffff;
}

.select_title:hover::before, .select_title:hover::after {
    background-color: #f3fdff;
}

.select_content {
    position: absolute;
    top: 40px;
    left: 3px;
    display: flex;
    flex-direction: column;
    width: calc(100% - 6px);
    background-color: #ffffff;
    border: 1px solid #c7ccd1;
    border-top: none;
    border-bottom-left-radius: 5px;
    border-bottom-right-radius: 5px;
    transition: all 0.3s ease-out;
    opacity: 0;
    z-index: 8;
}

.select_input {
    display: none;
}

.select_input:checked + label {
    background-color: #dedede;
}

.select_input:disabled + label {
    opacity: 0.6;
    pointer-events: none;
}

.select_label {
    display: flex;
    align-items: center;
    width: 100%;
    height: 40px;
    max-height: 0;
    padding: 0 20px;
    transition: all 0.1s ease-out;
    cursor: pointer;
    overflow: hidden;

}

.select_label + input + .select_label {
    border-top: 0 solid #C7CCD160;
}

.select_label:hover {
    background-color: #38b0ff !important;
    color: #ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

upd.: если нужно будет значение "передавать дальше" то можно, к примеру, в функцию которая создаёт эл-т вторым параметром передавать class/name, по которому можно будет взять значение, и добавить этот параметр в тот же input.

upd. добавил вариант с 1 js методом + html...

function ready() {
    const selects = document.querySelectorAll('.select');
    selects.forEach(function (select){
        const select_title = select.querySelector('.select_title');
        const select_labels = select.querySelectorAll('.select_label');

        select_title.addEventListener('click', () => {
            if ('active' === select.dataset.state) {
                select.dataset.state = '';
            } else {
                select.dataset.state = 'active';
            }
        });

        // Close when click to option
        for (let i = 0; i < select_labels.length; i++) {
            select_labels[i].addEventListener('click', (evt) => {
                select_title.textContent = evt.target.textContent;
                select.dataset.state = '';
            });
        }

        ///close outside the element
        window.addEventListener('mouseup', e => select.dataset.state = '');
    });
}
document.addEventListener("DOMContentLoaded", ready);
.select {
    position: relative;
    width: 300px;
    height: 40px;
    margin: 20px 0;
}

.select[data-state="active"] .select_title::before {
    transform: translate(-3px, -50%) rotate(-45deg);
}

.select[data-state="active"] .select_title::after {
    transform: translate(3px, -50%) rotate(45deg);
}

.select[data-state="active"] .select_content {
    opacity: 1;
}

.select[data-state="active"] .select_label + .select_input + .select_label {
    max-height: 40px;
    border-top-width: 1px;
}

.select_title {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    padding: 8px 16px;
    border-radius: 5px;
    border: solid 1px #c7ccd1;
    cursor: pointer;
    background-color: #38b0ff;
    color: #e9e9e9;
}

.select_title::before, .select_title::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 16px;
    display: block;
    width: 10px;
    height: 2px;
    transition: all 0.3s ease-out;
    background-color: #e9e9e9;
    transform: translate(-3px, -50%) rotate(45deg);
}

.select_title::after {
    transform: translate(3px, -50%) rotate(-45deg);
}

.select_title:hover {
   color: #ffffff;
}

.select_title:hover::before, .select_title:hover::after {
    background-color: #f3fdff;
}

.select_content {
    position: absolute;
    top: 40px;
    left: 3px;
    display: flex;
    flex-direction: column;
    width: calc(100% - 6px);
    background-color: #ffffff;
    border: 1px solid #c7ccd1;
    border-top: none;
    border-bottom-left-radius: 5px;
    border-bottom-right-radius: 5px;
    transition: all 0.3s ease-out;
    opacity: 0;
    z-index: 8;
}

.select_input {
    display: none;
}

.select_input:checked + label {
    background-color: #dedede;
}

.select_input:disabled + label {
    opacity: 0.6;
    pointer-events: none;
}

.select_label {
    display: flex;
    align-items: center;
    width: 100%;
    height: 40px;
    max-height: 0;
    padding: 0 20px;
    transition: all 0.1s ease-out;
    cursor: pointer;
    overflow: hidden;

}

.select_label + input + .select_label {
    border-top: 0 solid #C7CCD160;
}

.select_label:hover {
    background-color: #38b0ff !important;
    color: #ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="select" data-state="">
    <div class="select_title type_request" value="" data-default="">1-4</div>
    <div class="select_content">
        <input id="option0" class="select_input" value="" type="radio" name="singleSelect" />
        <label for="option0" class="select_label">1-4</label>
        <input id="option1" class="select_input type_request" value="1" type="radio" name="singleSelect" />
        <label for="option1" class="select_label">Один</label>
        <input id="option2" class="select_input type_request" value="2" type="radio" name="singleSelect" />
        <label for="option2" class="select_label">Два</label>
        <input id="option3" class="select_input type_request" value="3" type="radio" name="singleSelect" />
        <label for="option3" class="select_label">Три</label>
        <input id="option4" class="select_input type_request" value="4" type="radio" name="singleSelect" />
        <label for="option4" class="select_label">Четыре</label>
    </div>
</div>

<div class="select" data-state="">
    <div class="select_title type_request" value="" data-default="">4-7</div>
    <div class="select_content">
        <input id="option0" class="select_input" value="" type="radio" name="singleSelect" />
        <label for="option0" class="select_label">4-7</label>
        <input id="option1" class="select_input type_request" value="1" type="radio" name="singleSelect" />
        <label for="option1" class="select_label">Четыре</label>
        <input id="option2" class="select_input type_request" value="2" type="radio" name="singleSelect" />
        <label for="option2" class="select_label">Пять</label>
        <input id="option3" class="select_input type_request" value="3" type="radio" name="singleSelect" />
        <label for="option3" class="select_label">Шесть</label>
        <input id="option4" class="select_input type_request" value="4" type="radio" name="singleSelect" />
        <label for="option4" class="select_label">Семь</label>
    </div>
</div>

<div class="select" data-state="">
    <div class="select_title type_request" value="" data-default="">Да/Нет?</div>
    <div class="select_content">
        <input id="option0" class="select_input" value="" type="radio" name="singleSelect" />
        <label for="option0" class="select_label">Да/Нет?</label>
        <input id="option0" class="select_input type_request" value="1" type="radio" name="singleSelect" />
        <label for="option0" class="select_label">Да</label>
        <input id="option1" class="select_input type_request" value="2" type="radio" name="singleSelect" />
        <label for="option1" class="select_label">Нет</label>
    </div>
</div>

→ Ссылка