I am working on the "tags selector" below in JavaScript, without using any plugin.
var results = []
function renderTags() {
var tagsContainer = document.querySelector(".tags-list")
var tags = ""
results = [...new Set(results)]
results = results.sort()
results.forEach(function(tag) {
var tag = `<li class="tag"
><span class="value">${tag}</span>
<button>×</button>
</li>`
tags = tags + tag
tagsContainer.innerHTML = tags
})
}
function selectTags(event) {
var select = event.target
var options = select && select.options
for (var i = 0; i < options.length; i++) {
if (options[i].selected) {
results.push(options[i].text)
} else {
results = results.filter((tag) => tag !== options[i].text)
}
}
renderTags()
}
function removeTags(event) {
var elementToRemove = null
var clickedTag = event.target
var selectBox = clickedTag.closest("div").querySelector("select")
var selectOptions = Array.from(selectBox.options)
var tagText = clickedTag.parentNode.querySelector(".value").textContent
results = results.filter((tag) => tag != tagText)
if (!results.includes(tagText)) {
elementToDeselect = selectOptions.find((o) => o.text == tagText)
elementToDeselect.setAttribute('selected', false)
}
renderTags()
}
const tagSelector = document.getElementById("fruits")
const tagsList = document.querySelector(".tags-list")
tagsList.addEventListener("click", function(event) {
if (event.target.parentNode.tagName === "LI") {
removeTags(event)
}
})
tagSelector.addEventListener("change", selectTags)
.tags-list {
margin: 0 0 4px 0;
min-height: 40px;
list-style-type: none;
}
.tags-list .tag {
line-height: 1;
white-space: nowrap;
background: #f2f2f2;
border: 1px solid #e6e3e3;
display: inline-flex;
align-items: center;
border-radius: 999px;
font-size: 13px;
padding: 3px 8px;
margin-bottom: 1px;
}
.tags-list .tag button {
background: #ff9292;
color: #720000;
border: none;
width: 18px;
height: 18px;
font-size: 12px;
display: inline-flex;
justify-content: center;
align-items: center;
margin-left: 6px;
border-radius: 50%;
}
select[multiple] option {
padding: 4px 8px;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div class="form-group">
<label for="fruits">Fruits</label>
<ul class="form-control tags-list">
<p class="m-0">Select one or more tags from the list below</p>
</ul>
<select id="fruits" name="fruits" class="form-select" multiple>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="blackberry">Blackberry</option>
<option value="blueberry">Blueberry</option>
<option value="watermelon">Watermelon</option>
</select>
</div>
</div>
The removeTags(event)
method should not only remove a tag from the top tags list, but deselect the option corresponding to the removed tag from the <select multiple>
element.
For this purpose, I have used:
if (!results.includes(tagText)) {
elementToDeselect = selectOptions.find((o) => o.text == tagText)
elementToDeselect.setAttribute('selected', false)
}
For a reason I was unable to figure out, the option corresponding to the removed tag appears selected.
How can I obtain the desired result?
Use elementToDeselect.selected = false
to set the selected
property (which ends up setting the attribute), not setAttribute
(which would just set a HTML attribute).