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="/[email protected]/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>
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/npm/[email protected]/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?
Share Improve this question edited Feb 23 at 14:19 Razvan Zamfir asked Feb 23 at 14:13 Razvan ZamfirRazvan Zamfir 4,7047 gold badges47 silver badges283 bronze badges 04 Answers
Reset to default 6Use elementToDeselect.selected = false
to set the selected
property (which ends up setting the attribute), not setAttribute
(which would just set a HTML attribute).
You are looking for
Array.from(selectBox.options).filter(item => (item.innerText === tagText))[0].selected = false;
Explanation:
- you get the array
- from the options of the select box
- and you filter it
- by the inner text matching the tag text
- you will have exactly one element as a result if your texts are unique and all tag texts are among the options
- so you set the selected attribute of the 0th element of the filter result to false
Snippet:
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 tagText = clickedTag.parentNode.querySelector(".value").textContent
results = results.filter((tag) => tag != tagText)
Array.from(selectBox.options).filter(item => (item.innerText === tagText))[0].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/npm/[email protected]/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>
a "shorter"(?) way to do that...
const
tagSelector = document.querySelector('#fruits')
, tagsContainer = document.querySelector('ul.tags-list')
;
tagSelector.value = ''
;
tagSelector.addEventListener('click', evt =>
{
if (!evt.target.matches('option')) return
;
while (tagsContainer.firstChild)
tagsContainer.removeChild(tagsContainer.lastChild)
;
[...tagSelector.selectedOptions].forEach( opt =>
{
let newTag = new Range().createContextualFragment(
`<li class="tag">
<span class="value">${opt.value}</span>
<button>×</button>
</li>`)
;
tagsContainer.append(newTag);
});
});
tagsContainer.addEventListener('click', ({target: li_btnTag}) =>
{
if (!li_btnTag.matches('li.tag > button')) return
;
let
liTag = li_btnTag.closest('li')
, outValue = liTag.querySelector('span').textContent
;
tagsContainer.removeChild(liTag);
tagSelector.querySelector(`option[value="${outValue}"]`).selected = false;
})
/* ADDED */
ul.form-control.tags-list:empty::before {
content : 'Select one or more tags from the list below';
pointer-events : none;
color : lightslategrey;
}
/* ----- */
.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/npm/[email protected]/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"></ul>
<select id="fruits" name="fruits" class="form-select" multiple size="5">
<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>
Don't use setAttribute
here.
I would also suggest to just have a render function that aligns both widgets with what you have in a memory Set, and let the two event handlers adapt that Set, after which they can synchronise the display with a call to that render function:
const tagsList = document.querySelector(".tags-list")
const tagSelector = document.getElementById("fruits")
let selectedTags = new Set; // This has the current state
function render() {
tagsList.innerHTML = [...selectedTags].sort().map(tag =>
`<li class="tag"
><span class="value">${tag}</span>
<button>×</button>
</li>`
).join("") || '<li class="text-muted">Select one or more tags from the list below</li>';
for (const option of tagSelector.options) {
option.selected = selectedTags.has(option.textContent);
}
}
tagsList.addEventListener("click", function(event) {
if (event.target.tagName !== "BUTTON") return;
const span = event.target.closest("LI").children[0];
selectedTags.delete(span.textContent);
render();
});
tagSelector.addEventListener("change", function () {
selectedTags = new Set(
Array.from(tagSelector.options)
.filter(option => option.selected)
.map(option => option.textContent)
);
render();
});
// Define the initial selected items, if any:
selectedTags.add("Banana");
render();
.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/npm/[email protected]/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">
<li class="text-muted">Select one or more tags from the list below</li>
</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>
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1741316067a4340212.html
评论列表(0条)