Сообщение от gunner17
|
При вводе запятой или нажатии клавиши Enter весь введённый текст должен превратиться в тег.
|
Смотрите пример ниже.
Сообщение от gunner17
|
По клику на тег, он должен удалиться из текстового поля.
|
Сделал, чтобы удалялся при долгом нажатии на экран или вызове контекстного меню вторичной кнопкой мыши, поскольку очень легко удаляется при обычном нажатии.
Также можно перемещаться между тегами при помощи стрелок и теги удаляются, как обычный текст, при помощи клавиш BackSpace и Delete.
<body><script>
class TagsField extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
span {
background-color: #4CAF50;
color: white;
text-shadow: 0 1px rgba(0, 0, 0, 0.5);
box-shadow: 0 0.1em 0.1em rgba(0, 0, 0, 0.2);
border-radius: 1em;
padding: 0.45em 0.8em;
margin: 0.1em;
display: inline-block;
}
label {
border: 1px solid #ccc;
border-radius: .5em;
padding: .5em;
display: inline-flex;
font: bolder 100% sans-serif;
flex-wrap: wrap;
position: relative;
}
label::before {
content: "Input tags";
position: absolute;
pointer-events: none;
line-height: 0;
top: 0;
text-shadow: 2px 0 white, -2px 0 white;
}
input {
all: unset;
min-width: 5em;
width: 0;
padding: 0.45em 0.8em;
margin: 0.1em;
}
</style>
<label>
<input>
</label>
`;
this.input = this.shadowRoot.querySelector("input");
this.label = this.shadowRoot.querySelector("label");
this.input.value = "\u200b\u200b";
this.inputHandler = this.inputHandler.bind(this);
this.contextMenuHandler = this.contextMenuHandler.bind(this);
"input focus keydown keyup touchstart touchmove click".split(" ").forEach(type => {
this.input.addEventListener(type, this.inputHandler);
});
this.label.addEventListener("contextmenu", this.contextMenuHandler);
}
inputHandler(event) {
if(event.type !== "focus" && event.type !== "click") {
if(
this.input.selectionStart === 0 &&
this.input.selectionEnd === 0
) {
if(this.input.previousElementSibling) {
if(this.input.value.length === 1 || !this.input.value.startsWith("\u200b"))
this.label.removeChild(this.input.previousElementSibling);
else
this.label.insertBefore(
this.input.previousElementSibling,
this.input.nextElementSibling
);
}
}
if(
this.input.selectionStart === this.input.value.length &&
this.input.selectionEnd === this.input.value.length
) {
if(this.input.nextElementSibling) {
if(this.input.value.length === 1 || !this.input.value.endsWith("\u200b"))
this.label.removeChild(this.input.nextElementSibling);
else
this.label.insertBefore(
this.input.nextElementSibling,
this.input
);
}
}
}
var rawTags = this.input.value.slice(1, -1);
if(event.type === "keyup" && event.key === "Enter") {
rawTags += ",";
}
const tags = rawTags.split(",");
const editableTag = tags.pop();
tags.filter(tag => tag).map(this.constructor.createTag).forEach(tag => {
this.label.insertBefore(tag, this.input);
});
this.input.value = `\u200b${editableTag}\u200b`;
this.input.style.width = "0";
this.input.style.width = this.input.scrollWidth + "px";
this.input.selectionStart = Math.max(1, Math.min(this.input.selectionStart, this.input.value.length - 1));
this.input.selectionEnd = Math.max(1, Math.min(this.input.selectionEnd, this.input.value.length - 1));
}
contextMenuHandler(event) {
if(event.target.nodeName === "SPAN") {
if(confirm("Remove tag?")) {
event.preventDefault();
this.label.removeChild(event.target);
}
}
}
static createTag(tag) {
const element = document.createElement("span");
element.textContent = tag;
return element;
}
get tags() {
return Array.from(this.label.querySelectorAll("span")).map(element => element.textContent);
}
}
customElements.define("tags-field", TagsField);
// Поле для тегов создаётся программно следующим образом:
let tagField = document.createElement("tags-field");
document.body.append(tagField);
</script>