#javascript #html #jquery
#javascript #HTML #jquery
Вопрос:
Я пытаюсь изменить свое значение <select><option value=""></option></select>
на основе созданной мной пользовательской опции выбора. Поскольку параметры выбора не могут быть настроены так легко, мы используем JS, jQuery для его настройки. Поскольку я плохо разбираюсь в JS, я просто искал видео и сделал то же самое, чтобы изменить свой вариант выбора. Теперь проблема в том, что когда я выбираю опцию из опции пользовательского выбора, значение исходной опции выбора не меняется, и из-за этого я не могу отправить свои данные формы.
Вот опция выбора по умолчанию
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">Test Product 2</option>
<option value="2">Test Product 1</option>
<option value="1">Test Product</option>
</select>
</div>
</div>
</div>
Вот пользовательский, созданный с помощью js
<div class="card my-5">
<div class="card-body">
<div class="dropdown">
<div class="dropdown__selected">---------</div>
<div class="dropdown__menu">
<input placeholder="Search..." type="text" class="dropdown__menu_search">
<div class="dropdown__menu_items">
<div class="dropdown__menu_item selected" data-value="">---------</div>
<div class="dropdown__menu_item" data-value="3">Test Product 2</div>
<div class="dropdown__menu_item" data-value="2">Test Product 1</div>
<div class="dropdown__menu_item" data-value="1">Test Product</div>
</div>
</div>
</div>
</div>
</div>
JS-код для отображения вышеупомянутой пользовательской опции выбора
const dropdowns = document.querySelectorAll('.select-menu, #id_weight_1');
const form = document.querySelector('form')
if(dropdowns.length > 0){
dropdowns.forEach(dropdown => {
createCustomDropdown(dropdown);
});
}
// if(form !=null){
// form.addEventListener('submit', (e) => {
// e.preventDefault();
// const a = form.querySelector('[name=product]');
// console.log('Select:', a.options[a.selectedIndex].text);
// });
// }
// Create custom dropdown
function createCustomDropdown(dropdown) {
// Get all options and convert them from nodelist to array
const options = dropdown.querySelectorAll('option');
const optionsArr = Array.prototype.slice.call(options);
// Create custom dropdown element and add class dropdown to it
// Insert it in the DOM after the select field
const customDropdown = document.createElement('div');
customDropdown.classList.add('dropdown');
dropdown.insertAdjacentElement('afterend', customDropdown);
// Create element for selected option
// Add class to this element, text from the first option in select field and append it to custom dropdown
const selected = document.createElement('div');
selected.classList.add('dropdown__selected');
selected.textContent = optionsArr[0].textContent;
customDropdown.appendChild(selected);
// Create element for dropdown menu, add class to it and append it to custom dropdown
// Add click event to selected element to toggle dropdown menu
const menu = document.createElement('div');
menu.classList.add('dropdown__menu');
customDropdown.appendChild(menu);
selected.addEventListener('click', toggleDropdown.bind(menu));
// Create serach input element
// Add class, type and placeholder to this element and append it to menu element
const search = document.createElement('input');
search.placeholder = 'Search...';
search.type = 'text';
search.classList.add('dropdown__menu_search');
menu.appendChild(search);
// Create wrapper element for menu items, add class to it and append to menu element
const menuItemsWrapper = document.createElement('div');
menuItemsWrapper.classList.add('dropdown__menu_items');
menu.appendChild(menuItemsWrapper);
// Loop through all options and create custom option for each option and append it to items wrapper element
// Add click event for each custom option to set clicked option as selected option
optionsArr.forEach(option => {
const item = document.createElement('div');
item.classList.add('dropdown__menu_item');
item.dataset.value = option.value;
item.textContent = option.textContent;
menuItemsWrapper.appendChild(item);
item.addEventListener('click', setSelected.bind(item, selected, dropdown, menu));
});
// Add selected class to first custom option
menuItemsWrapper.querySelector('div').classList.add('selected');
// Add input event to search input element to filter items
// Add click event to document element to close custom dropdown if clicked outside of it
// Hide original dropdown(select)
search.addEventListener('input', filterItems.bind(search, optionsArr, menu));
document.addEventListener('click', closeIfClickedOutside.bind(customDropdown, menu));
// dropdown.style.display = 'none';
}
// Toggle dropdown
function toggleDropdown() {
// Check if dropdown is opened and if it is close it, otherwise open it and focus search input
if(this.offsetParent !== null) {
this.style.display = 'none';
}else {
this.style.display = 'block';
this.querySelector('input').focus();
}
}
// Set selected option
function setSelected(selected, dropdown, menu) {
// Get value and label from clicked custom option
const value = this.dataset.value;
const label = this.textContent;const th =
// Change the text on selected element
// Change the value on select field
selected.textContent = label;
dropdown.value = value;
// Close the menu
// Reset search input value
// Remove selected class from previously selected option and show all divs if they were filtered
// Add selected class to clicked option
menu.style.display = 'none';
menu.querySelector('input').value = '';
menu.querySelectorAll('div').forEach(div => {
if(div.classList.contains('selected')) {
div.classList.remove('selected');
}
if(div.offsetParent === null) {
div.style.display = 'block';
}
});
this.classList.add('selected');
}
// Filter items
function filterItems(itemsArr, menu) {
// Get all custom options
// Get the value of search input and convert it to all lowercase characters
// Get filtered items
// Get the indexes of filtered items
const customOptions = menu.querySelectorAll('.dropdown__menu_items div');
const value = this.value.toLowerCase();
const filteredItems = itemsArr.filter(item => item.label.toLowerCase().includes(value));
const indexesArr = filteredItems.map(item => itemsArr.indexOf(item));
// Check if option is not inside indexes array and hide it and if it is inside indexes array and it is hidden show it
itemsArr.forEach(option => {
if(!indexesArr.includes(itemsArr.indexOf(option))) {
customOptions[itemsArr.indexOf(option)].style.display = 'none';
}else {
if(customOptions[itemsArr.indexOf(option)].offsetParent === null) {
customOptions[itemsArr.indexOf(option)].style.display = 'block';
}
}
});
}
// Close dropdown if clicked outside dropdown element
function closeIfClickedOutside(menu, e) {
if(e.target.closest('.dropdown') === null amp;amp; e.target !== this amp;amp; menu.offsetParent !== null) {
menu.style.display = 'none';
}
}
Как и здесь, у нас есть tow select options, #taxes
значение изменяется на основе значения #invoice
. Аналогичным образом я хочу, чтобы моя опция выбора работала подобным образом, поскольку у меня есть одна опция выбора, а пользовательская состоит из элементов div. Я надеюсь, вы не будете возражать, отвечая на мой вопрос, я действительно плохо разбираюсь в js, пожалуйста, помогите!
<select id="invoice">
<option value="no" selected>no invoice</option>
<option value="invoice-type-1">invoice type 1</option>
<option value="invoice-type-2">invoice type 2</option>
</select>
<select id="taxes">
<option value="0"> tax free</option>
<option value="19"> apply 19%</option>
</select>
$('#invoice').change(function(){
if($(this).val()=='no')
$('#taxes').val('0');
else
$('#taxes').val('19');
});
Комментарии:
1. вы можете сделать выбранный выбор / опцию на основе значения, вы не можете сделать выбранный div таким.
2. @DevsiOdedra На самом деле я тебя не понял. Выбранный div изменяется в зависимости от выбранного мной параметра. Когда я выбираю опцию из выпадающих элементов, я хочу, чтобы исходное значение тега select было соответствующим образом изменено.
3. @DevsiOdedra Как вы говорите, мне все равно не нужно, чтобы это работало? Я хочу использовать пользовательскую опцию выбора, например, если у нас есть 1000 опций, прокручивающих все из них, и выбор опции будет намного сложнее, верно
4. хорошо, на основе пользовательского выпадающего списка вы хотите создать selected Productdropdown
5. @DevsiOdedra На основе пользовательского выпадающего списка я также хочу, чтобы он выбирал значение параметра выбора по умолчанию. Я имею в виду, что когда я выбираю тестовый продукт 1 из
data-value="1"
пользовательского выпадающего списка, я хочу, чтобы он автоматически выбирал тестовый продукт 1 из опции выбора по умолчанию, которая имеетvalue="1"
, чтобы я мог отправить данные формы.
Ответ №1:
Смотрите, как показано ниже, выбор на основе пользовательского выпадающего списка
const dropdowns = document.querySelectorAll('.select-menu, #id_weight_1');
const form = document.querySelector('form')
if(dropdowns.length > 0){
dropdowns.forEach(dropdown => {
createCustomDropdown(dropdown);
});
}
// if(form !=null){
// form.addEventListener('submit', (e) => {
// e.preventDefault();
// const a = form.querySelector('[name=product]');
// console.log('Select:', a.options[a.selectedIndex].text);
// });
// }
// Create custom dropdown
function createCustomDropdown(dropdown) {
// Get all options and convert them from nodelist to array
const options = dropdown.querySelectorAll('option');
const optionsArr = Array.prototype.slice.call(options);
// Create custom dropdown element and add class dropdown to it
// Insert it in the DOM after the select field
const customDropdown = document.createElement('div');
customDropdown.classList.add('dropdown');
dropdown.insertAdjacentElement('afterend', customDropdown);
// Create element for selected option
// Add class to this element, text from the first option in select field and append it to custom dropdown
const selected = document.createElement('div');
selected.classList.add('dropdown__selected');
selected.textContent = optionsArr[0].textContent;
customDropdown.appendChild(selected);
// Create element for dropdown menu, add class to it and append it to custom dropdown
// Add click event to selected element to toggle dropdown menu
const menu = document.createElement('div');
menu.classList.add('dropdown__menu');
customDropdown.appendChild(menu);
selected.addEventListener('click', toggleDropdown.bind(menu));
// Create serach input element
// Add class, type and placeholder to this element and append it to menu element
const search = document.createElement('input');
search.placeholder = 'Search...';
search.type = 'text';
search.classList.add('dropdown__menu_search');
menu.appendChild(search);
// Create wrapper element for menu items, add class to it and append to menu element
const menuItemsWrapper = document.createElement('div');
menuItemsWrapper.classList.add('dropdown__menu_items');
menu.appendChild(menuItemsWrapper);
// Loop through all options and create custom option for each option and append it to items wrapper element
// Add click event for each custom option to set clicked option as selected option
optionsArr.forEach(option => {
const item = document.createElement('div');
item.classList.add('dropdown__menu_item');
item.dataset.value = option.value;
item.textContent = option.textContent;
menuItemsWrapper.appendChild(item);
item.addEventListener('click', setSelected.bind(item, selected, dropdown, menu));
});
// Add selected class to first custom option
menuItemsWrapper.querySelector('div').classList.add('selected');
// Add input event to search input element to filter items
// Add click event to document element to close custom dropdown if clicked outside of it
// Hide original dropdown(select)
search.addEventListener('input', filterItems.bind(search, optionsArr, menu));
document.addEventListener('click', closeIfClickedOutside.bind(customDropdown, menu));
// dropdown.style.display = 'none';
}
// Toggle dropdown
function toggleDropdown() {
// Check if dropdown is opened and if it is close it, otherwise open it and focus search input
if(this.offsetParent !== null) {
this.style.display = 'none';
}else {
this.style.display = 'block';
this.querySelector('input').focus();
}
}
// Set selected option
function setSelected(selected, dropdown, menu) {
// Get value and label from clicked custom option
const value = this.dataset.value;
console.log(value);
document.getElementById("id_product").selectedIndex = value;
const label = this.textContent;const th =
// Change the text on selected element
// Change the value on select field
selected.textContent = label;
dropdown.value = value;
// Close the menu
// Reset search input value
// Remove selected class from previously selected option and show all divs if they were filtered
// Add selected class to clicked option
menu.style.display = 'none';
menu.querySelector('input').value = '';
menu.querySelectorAll('div').forEach(div => {
if(div.classList.contains('selected')) {
div.classList.remove('selected');
}
if(div.offsetParent === null) {
div.style.display = 'block';
}
});
this.classList.add('selected');
}
// Filter items
function filterItems(itemsArr, menu) {
// Get all custom options
// Get the value of search input and convert it to all lowercase characters
// Get filtered items
// Get the indexes of filtered items
const customOptions = menu.querySelectorAll('.dropdown__menu_items div');
const value = this.value.toLowerCase();
const filteredItems = itemsArr.filter(item => item.label.toLowerCase().includes(value));
const indexesArr = filteredItems.map(item => itemsArr.indexOf(item));
// Check if option is not inside indexes array and hide it and if it is inside indexes array and it is hidden show it
itemsArr.forEach(option => {
if(!indexesArr.includes(itemsArr.indexOf(option))) {
customOptions[itemsArr.indexOf(option)].style.display = 'none';
}else {
if(customOptions[itemsArr.indexOf(option)].offsetParent === null) {
customOptions[itemsArr.indexOf(option)].style.display = 'block';
}
}
});
}
// Close dropdown if clicked outside dropdown element
function closeIfClickedOutside(menu, e) {
if(e.target.closest('.dropdown') === null amp;amp; e.target !== this amp;amp; menu.offsetParent !== null) {
menu.style.display = 'none';
}
}
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">Test Product 2</option>
<option value="2">Test Product 1</option>
<option value="1">Test Product</option>
</select>
</div>
</div>
</div>
Ответ №2:
Я не знаю, почему вы настаиваете на том, чтобы в качестве вашего второго виджета было выбрано «пользовательское» поле выбора. Вы можете упростить жизнь, если используете стандартное поле выбора и синхронизируете первое, выполнив что-то простое, как показано ниже:
document.getElementById('id_custom').addEventListener('change',ev=>
document.getElementById('id_product').value=ev.target.value )
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select>
</div>
</div>
</div>
<div class="card my-5">
<div class="card-body">
<label>custom<br>
<select id="id_custom">
<option value="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select></label>
</div>
</div>
Если вы хотите, чтобы изменение было синхронизировано в обоих направлениях, вы могли бы сделать это:
document.getElementById('filt').oninput=ev=>
[...document.getElementById('id_custom').children].forEach(o=>
o.classList.toggle('hidden',o.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase())<0)
)
document.getElementById('container').addEventListener('change',ev=>
ev.target.tagName=='SELECT' amp;amp; [...document.querySelectorAll('SELECT')].forEach(s=>s.value=ev.target.value)
)
.hidden {display:none}
<div id="container">
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select>
</div>
</div>
</div>
<div class="card my-5">
<div class="card-body">
<label>custom<br>
<input type="text" id="filt" placeholder="filter ..."><br>
<select id="id_custom">
<option value="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select></label>
</div>
</div>
</div>
В этом фрагменте я также включил простой механизм фильтрации для второго поля выбора. Дополнительная библиотека или фреймворк не требуются.
Комментарии:
1. Да, сэр. Но причина в том, что я не использую какой-либо фреймворк интерфейса, такой как react, если у меня есть 1000 вариантов, то выбрать конкретный будет сложно. Думаю, небольшая настройка и добавление фильтра поиска сверху очень помогают.
2. Хорошо, я это вижу, но вы можете сделать то же самое и со стандартным элементом select … 😉
3. Действительно? Не могли бы вы рассказать мне, как или предложить мне какую-либо ссылку или документы для просмотра.
4. Смотрите мой измененный ответ (второй, скрытый фрагмент, нижняя часть)!
5. Спасибо за ответ. Извините, я не смотрел на это раньше.