Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Как сделать поле ввода тегов\меток на чистом js (https://javascript.ru/forum/events/77063-kak-sdelat-pole-vvoda-tegov%5Cmetok-na-chistom-js.html)

gunner17 20.03.2019 21:52

Как сделать поле ввода тегов\меток на чистом js
 
Нужно сделать поле ввода тегов на чистом js без библиотек как в данном примере https://ruseller.com/lessons.php?id=992&rub=32 При вводе запятой или нажатии клавиши Enter весь введённый текст должен превратиться в тег. После можно продолжить вводить следующий тег и т.д. По клику на тег, он должен удалиться из текстового поля. Поле для тегов должно создаваться программно следующим образом::help:



let tagField = createTagsField();
document.body.append(tagField.element);

Malleys 21.03.2019 08:32

Цитата:

Сообщение от 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>

рони 21.03.2019 09:01

Malleys,
:thanks:


Часовой пояс GMT +3, время: 17:11.