Reputation: 9
Just wanted to know easiest way to achieve this
Upvotes: 0
Views: 659
Reputation: 10221
Perhaps something like this:
(()=>
{
const formEls = document.querySelectorAll(".input-tags");
for(let i = 0; i < formEls.length; i++)
{
const formEl = formEls[i],
inputEl = document.createElement("input"),
tagsEl = document.createElement("span"),
listEl = document.createElement("datalist");
formEl.tags = [];
Object.defineProperties(formEl, {
list: {
get(){return getData(this, "list")},
set(val){this.dataset.list = val}
},
tags: {
get(){return getData(this, "tags")},
set(val){this.dataset.tags = val}
},
value:
{
get(){return this.dataset.value || ""},
set(val){this.dataset.value = val}
}
});
const list = formEl.list;
listEl.id = "input-tags-datalist" + i;
inputEl.setAttribute("list", listEl.id);
inputEl.type = "text";
tagsEl.className = "tags";
for(let i = 0, optionEl = document.createElement("option"); i < list.length; i++)
{
optionEl = optionEl.cloneNode(false);
optionEl.value = list[i];
listEl.appendChild(optionEl);
}
formEl.appendChild(tagsEl);
formEl.appendChild(inputEl);
formEl.appendChild(listEl);
inputEl._isClicked = true;
inputEl.addEventListener("keydown", e => inputEl._isClicked = !e.keyCode || e.keyCode==13);
inputEl.addEventListener("keyup", e => inputEl._isClicked = true);
inputEl.addEventListener("input", e =>
{
formEl.value = inputEl.value;
if (!inputEl._isClicked && !inputEl.value.match(/(^[^"']+ $)|(^(["']).+\3$)/))
{
dispatchEvent(formEl, "input");
return inputWidth(formEl);
}
const val = inputEl.value.replace(/^\s*((["'])([^"']+)\2|([^"']+)\s+)$/, "$4$3").replace(/[^\w -_]+/g, "").replace(/[ ]{2,}/g, " ");
if (formEl.dataset.autotags !== undefined || formEl.list.indexOf(val) != -1)
{
inputEl.value = val;
addTag(inputEl);
}
formEl.value = inputEl.value;
dispatchEvent(formEl, "input");
inputWidth(formEl);
});//inputEl.oninput()
tagsEl.addEventListener("click", e =>
{
if (!e.target.parentNode.classList.contains("tag"))
return;
const tag = e.target.parentNode.textContent,
list = formEl.list,
tags = formEl.tags,
index = list.indexOf(tag),
optionEl = listEl.children[index];
if (optionEl.classList.contains("new"))
{
list.splice(index, 1);
optionEl.parentNode.removeChild(optionEl);
}
else
optionEl.disabled = false;
tags.splice(tags.indexOf(tag), 1);
formEl.tags = tags;
formEl.list = list;
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
inputWidth(formEl);
e.stopPropagation();
formEl.click();
dispatchEvent(formEl, "input");
});//tagsEl.onclick()
formEl.addEventListener("click", e => inputEl.focus());
inputWidth(formEl);
}
function dispatchEvent(el, type, opts)
{
return el.dispatchEvent(new Event(type, opts));
}
function inputWidth(formEl)
{
const inputEl = formEl.querySelector("input");
inputEl.style.width = "1em"; //min width
const inputStyle = window.getComputedStyle(inputEl),
formStyle = window.getComputedStyle(inputEl.parentNode),
inputRect = inputEl.getBoundingClientRect(),
formRect = inputEl.parentNode.getBoundingClientRect(),
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
ctx.font = inputStyle.font;
const widthText = (ctx.measureText(inputEl.value).width
+ parseFloat(inputStyle.paddingLeft)
+ parseFloat(inputStyle.paddingRight)
+ parseFloat(inputStyle.textIndent)
+ parseFloat(inputStyle.borderLeftWidth)
+ parseFloat(inputStyle.borderRightWidth)
+ 1
),
widthBox = formRect.right - inputRect.left - parseFloat(formStyle.paddingLeft) - parseFloat(formStyle.paddingRight) - 1;
inputEl.style.width = Math.max(widthText, widthBox) + "px";
}
function getData(el, key)
{
return el.dataset[key] ? el.dataset[key].split(",") : [];
}
function addTag(input)
{
const formEl = input.parentNode,
tag = input.value.trim(),
list = formEl.list,
tags = formEl.tags;
if (tag === "" || tags.indexOf(tag) != -1)
return;
const tagsEl = formEl.querySelector(".tags"),
tagEl = document.createElement("span"),
datalistEl = formEl.querySelector("datalist");
if (formEl.dataset.autotags !== undefined && list.indexOf(tag) == -1)
{
const option = document.createElement("option");
option.value = tag;
option.className = "new";
datalistEl.appendChild(option);
list[list.length] = tag;
}
tags[tags.length] = tag;
formEl.list = list;
formEl.tags = tags;
const index = list.indexOf(tag);
datalistEl.children[index].disabled = true;
tagEl.className = "tag";
tagEl.textContent = tag;
tagEl.appendChild(document.createElement("span"));
tagsEl.appendChild(tagEl);
input.value = "";
}
})();
//example:
const test = document.getElementById("test");
test.addEventListener("input", e =>
{
if (e.target !== test)
return;
console.log('value:', test.value);
console.log("tags:", JSON.stringify(test.tags));
console.log("list:", JSON.stringify(test.list));
}, false);
.input-tags
{
display: inline-block;
border: 1px solid black;
font-size: 0.8em;
padding: 0.1em 0.1em 0.1em 0.05em;
width: 100%;
line-height: 1em;
}
.input-tags > input,
.input-tags > input:focus,
.input-tags > input:active
{
outline: none;
border: none;
margin: 0.15em 0;
vertical-align: middle;
max-width: 100%;
box-sizing: border-box;
}
.input-tags > input::-webkit-calendar-picker-indicator
{
display: none !important;
}
.input-tags > .tags
{
vertical-align: middle;
}
.input-tags .tags .tag
{
display: inline-block;
background-color: lightblue;
border: 1px solid blue;
border-radius: 2px;
font-family: "Segoe UI","Liberation Sans",sans-serif;
margin: 0.1em;
padding: 0 0.2em;
line-height: 1.3em;
}
.input-tags .tags .tag > span
{
margin: -0.05em -0.2em 0 0.05em;
cursor: pointer;
display: inline-block;
font-size: 1.3em;
transform: rotate(45deg);
border-radius: 2em;
line-height: 0.7em;
float: right;
}
.input-tags .tags .tag > span:before
{
content: "+";
position: relative;
top: -0.1em;
}
.input-tags .tags .tag > span:hover
{
background-color: #60B3CE;
}
/* stackoverflow console */
.as-console-wrapper{max-height:7em!important;}
<div style="display: grid; grid-template-columns: auto auto">
<span>Auto-add new tags, suggestions:</span>
<div style="display: inline-block; width: 50vw;">
<div id="test" class="input-tags" data-autotags data-list="test,sometag,SOMETAG,another tag,another tag2,another tag3,another,tag"></div>
</div>
<span>Auto-add new tags, no suggestions:</span>
<div style="display: inline-block; width: 50vw;">
<span class="input-tags" data-autotags></span>
</div>
<span>No new tags, suggestions:</span>
<div style="display: inline-block; min-width: 10em;">
<div class="input-tags" data-list="test,some tag,very long tag,blah"></div>
</div>
<div>
Upvotes: 2