<main>
<p>Separate your tags with a comma</p>
<div class="tags-input"></div>
<h1><span>Tags</span> Input</h1>
</main>
$white: #fff
$blue: #71ccd5
$dark-grey: #888
$grey: #bbb
$light-grey: #dfdfdf
html, body, main
height: 100%
main
max-width: 500px
margin: 0 auto
padding: 0 30px
display: flex
flex-direction: column-reverse
justify-content: center
font-family: 'Open Sans', Helvetica, Arial, sans-serif
h1
margin-bottom: 20px
font-size: 20px
font-weight: 300
text-align: center
text-transform: uppercase
letter-spacing: 5px
color: $grey
span
border: 2px solid $light-grey
padding: 3px 2px 3px 7px
border-radius: 6px
font-weight: 400
p
margin-top: 10px
font-size: 10px
font-weight: bold
color: $blue
text-align: center
text-transform: uppercase
letter-spacing: 1px
.tags-input
border: 2px solid $light-grey
border-radius: 40px
height: 40px
padding: 0 11px
font-size: 14px
display: flex
align-items: center
overflow: hidden
color: $dark-grey
span
background: $grey
margin-right: 7px
color: $white
padding: 4px 7px
border-radius: 20px
&[data-selected]
background: $blue
&::after
content: ''
background: $dark-grey
width: 1px
height: 19px
margin: -1px 0 0 -1px
display: none
animation: blink 0.5s infinite alternate
&:focus
border-color: $blue
outline: none
&[data-cursor]::after
display: block
@media screen and (max-width: 736px)
display: none
+ h1
color: $blue
span
border-color: $blue
@keyframes blink
0%
opacity: 1
49%
opacity: 1
50%
opacity: 0
100%
opacity: 0
View Compiled
var TagsInput = function(element) {
var self = this;
var initChar = "\u200B";
var initCharPattern = new RegExp(initChar, 'g');
var insert = function(element) {
if(self.textNode) self.element.insertBefore(element, self.textNode);
else self.element.appendChild(element);
};
var updateCursor = function() {
self.cursor = self.blank;
};
var keydown = function(event) {
if(event.keyCode == 188) {
event.preventDefault();
setTimeout(function() {
var text = self.text;
if(text) {
self.text = initChar;
self.add(text);
}
}, 1);
}
else if(event.keyCode == 8) {
if(self.text.replace(initCharPattern, '') == '') {
self.text = initChar+initChar;
if(self.selected) {
self.element.removeChild(self.selected);
}
else {
var tags = self.tags;
var keys = Object.keys(tags)
if(keys.length > 0) {
var tag = tags[keys[keys.length-1]];
tag.setAttribute('data-selected', '');
}
}
}
}
if(event.keyCode !== 8) {
if(self.selected) self.selected.removeAttribute('data-selected');
}
setTimeout(function() {
updateCursor();
}, 1);
};
var focus = function() {
updateCursor();
};
Object.defineProperties(this, {
element: {
get: function() {
return element;
},
set: function(v) {
if(typeof v == 'string') v = document.querySelector(v);
element = v instanceof Node ? v : document.createElement('div');
if(!element.className.match(/\btags-input\b/)) element.className += ' tags-input';
if(element.getAttribute('contenteditable') != 'true') element.setAttribute('contenteditable', 'true');
element.removeEventListener('keydown', keydown);
element.addEventListener('keydown', keydown);
element.removeEventListener('focus', focus);
element.addEventListener('focus', focus);
this.text = initChar;
}
},
tags: {
get: function() {
var element;
var elements = this.element.querySelectorAll('span');
var tags = {};
for(var i = 0; i < elements.length; i++) {
element = elements[i]
tags[element.innerText] = element;
}
return tags;
}
},
lastChild: {
get: function() {
return this.element.lastChild;
}
},
textNode: {
get: function() {
return this.element.lastChild instanceof Text ? this.element.lastChild : null;
}
},
text: {
get: function() {
return this.textNode ? this.textNode.data : null;
},
set: function(v) {
if(!this.textNode) this.element.appendChild(document.createTextNode(','));
this.textNode.data = v;
},
},
cursor: {
get: function() {
return this.element.getAttribute('data-cursor') !== null;
},
set: function(v) {
if(v) this.element.setAttribute('data-cursor', '');
else this.element.removeAttribute('data-cursor');
}
},
focused: {
get: function() {
return document.activeElement == this.element;
}
},
blank: {
get: function() {
return this.text.replace(initCharPattern, '') == '';
}
},
selected: {
get: function() {
return this.element.querySelector('span[data-selected]');
}
}
});
this.add = function(tag) {
tag = tag.replace(initCharPattern, '');
tag = tag.replace(/^\s+/, '').replace(/\s+$/, '');
tag = tag[0].toUpperCase()+tag.toLowerCase().slice(1);
if(tag != '' && this.tags[tag] === undefined) {
var element = document.createElement('span');
element.appendChild(document.createTextNode(tag));
element.setAttribute('contenteditable', 'false');
insert(element);
}
};
this.remove = function(tag) {
var element = this.tags[tag];
if(element) this.element.removeChild(element);
};
this.element = element;
};
var input = new TagsInput('.tags-input');
This Pen doesn't use any external JavaScript resources.