function htmlToElement(html) { const template = document.createElement('template'); template.innerHTML = html.trim(); return template.content.firstChild; } function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } class TagsInput { constructor(element, options = {}) { this.element = element; this.tags = []; this.options = options; this.maxTags = options.maxTags || 10; this.inputNode = null; this.containerNode = null; } attach() { this.element.style.display = 'none'; this.containerNode = htmlToElement('
'); this.inputNode = htmlToElement(''); this.containerNode.appendChild(this.inputNode); this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling); /* Handle addition and removal of tags via key-presses */ this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this)); /* Handle deletions by clicking the delete button */ this.containerNode.addEventListener('click', this._handleContainerClick.bind(this)); } detach() { this.tags.clear(); this.containerNode.remove(); this.element.style.display = 'inline-block'; } updateHiddenInputValue() { this.element.value = this.tags.join(','); } deleteTagNode(node) { this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1); node.remove(); /* Below the limit? Make sure the input is enabled. */ if (this.tags.length < this.maxTags) { this.inputNode.disabled = false; } } addTag(tagValue) { tagValue = tagValue.trim(); /* Tag value is probably not empty and we don't already have the same tag. */ if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) { this.tags.push(tagValue.toLowerCase()); this.inputNode.parentNode.insertBefore( htmlToElement('' + escapeHtml(tagValue) + ''), this.inputNode ); /* Too many tags, disable the input for now. */ if (this.tags.length >= this.maxTags) { this.inputNode.disabled = true; } } } _handleInputKeyUp(evt) { let tagValue = this.inputNode.value; if (evt.key === 'Backspace' && tagValue === '') { // Remove the child if (this.inputNode.previousSibling) { this.deleteTagNode(this.inputNode.previousSibling); this.updateHiddenInputValue(); } } else if (evt.key === ',') { this.addTag(tagValue); this.inputNode.value = ''; this.updateHiddenInputValue(); evt.preventDefault(); } } _handleContainerClick(evt) { if (evt.target && evt.target.classList.contains('delete')) { this.deleteTagNode(evt.target.closest('.tag')); this.updateHiddenInputValue(); } } } class Meme { constructor() { alert('xss'); } meme() { console.log('meme'); } } const meme = new Meme(); meme.meme(); new TagsInput(null);