Skip to content

Instantly share code, notes, and snippets.

@neisdev
Created December 30, 2021 14:45
Show Gist options
  • Save neisdev/5a69d98c7e7e97b44f80f91f46ab4c69 to your computer and use it in GitHub Desktop.
Save neisdev/5a69d98c7e7e97b44f80f91f46ab4c69 to your computer and use it in GitHub Desktop.
Tags input
<h3>Tags input with flexbox & javascript(IE9 inline-block)</h3>
<form action="" class="test" method="post">
<label for="exist-values">Exist values
<input type="text" id="exist-values" class="tagged form-control" data-removeBtn="true" name="tag-2" value="PC,Play Station 4,X-BOX One" placeholder="Add Platform">
</label>
<button type="submit" name="button">Send</button>
</form>
<button id="destroy">destroy</button>
<button id="add">add tags</button>
<button id="addArr">add tags array</button>
<button id="clear">clear tags</button>
<button id="get">get taggs</button>
// https://github.com/k-ivan/Tags
(function() {
'use strict';
// Helpers
function $$(selectors, context) {
return (typeof selectors === 'string') ? (context || document).querySelectorAll(selectors) : [selectors];
}
function $(selector, context) {
return (typeof selector === 'string') ? (context || document).querySelector(selector) : selector;
}
function create(tag, attr) {
var element = document.createElement(tag);
if(attr) {
for(var name in attr) {
if(element[name] !== undefined) {
element[name] = attr[name];
}
}
}
return element;
}
function whichTransitionEnd() {
var root = document.documentElement;
var transitions = {
'transition' : 'transitionend',
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'mozTransitionEnd',
'OTransition' : 'oTransitionEnd otransitionend'
};
for(var t in transitions){
if(root.style[t] !== undefined){
return transitions[t];
}
}
return false;
}
function oneListener(el, type, fn, capture) {
capture = capture || false;
el.addEventListener(type, function handler(e) {
fn.call(this, e);
el.removeEventListener(e.type, handler, capture)
}, capture);
}
function hasClass(cls, el) {
return new RegExp('(^|\\s+)' + cls + '(\\s+|$)').test(el.className);
}
function addClass(cls, el) {
if( ! hasClass(cls, el) )
return el.className += (el.className === '') ? cls : ' ' + cls;
}
function removeClass(cls, el) {
el.className = el.className.replace(new RegExp('(^|\\s+)' + cls + '(\\s+|$)'), '');
}
function toggleClass(cls, el) {
( ! hasClass(cls, el)) ? addClass(cls, el) : removeClass(cls, el);
}
function Tags(tag) {
var el = $(tag);
if(el.instance) return;
el.instance = this;
var type = el.type;
var transitionEnd = whichTransitionEnd();
var tagsArray = [];
var KEYS = {
ENTER: 13,
COMMA: 188,
BACK: 8
};
var isPressed = false;
var timer;
var wrap;
var field;
function init() {
// create and add wrapper
wrap = create('div', {
'className': 'tags-container',
});
field = create('input', {
'type': 'text',
'className': 'tag-input',
'placeholder': el.placeholder || ''
});
wrap.appendChild(field);
if(el.value.trim() !== '') {
hasTags();
}
el.type = 'hidden';
el.parentNode.insertBefore(wrap, el.nextSibling);
wrap.addEventListener('click', btnRemove, false);
wrap.addEventListener('keydown', keyHandler, false);
wrap.addEventListener('keyup', backHandler, false);
}
function hasTags() {
var arr = el.value.trim().split(',');
arr.forEach(function(item) {
item = item.trim();
if(~tagsArray.indexOf(item)) {
return;
}
var tag = createTag(item);
tagsArray.push(item);
wrap.insertBefore(tag, field);
});
}
function createTag(name) {
var tag = create('div', {
'className': 'tag',
'innerHTML': '<span class="tag__name">' + name + '</span>'+
'<button class="tag__remove">&times;</button>'
});
// var tagName = create('span', {
// 'className': 'tag__name',
// 'textContent': name
// });
// var delBtn = create('button', {
// 'className': 'tag__remove',
// 'innerHTML': '&times;'
// });
// tag.appendChild(tagName);
// tag.appendChild(delBtn);
return tag;
}
function btnRemove(e) {
e.preventDefault();
if(e.target.className === 'tag__remove') {
var tag = e.target.parentNode;
var name = $('.tag__name', tag);
wrap.removeChild(tag);
tagsArray.splice(tagsArray.indexOf(name.textContent), 1);
el.value = tagsArray.join(',')
}
field.focus();
}
function keyHandler(e) {
if(e.target.tagName === 'INPUT' && e.target.className === 'tag-input') {
var target = e.target;
var code = e.which || e.keyCode;
if(field.previousSibling && code !== KEYS.BACK) {
removeClass('tag--marked', field.previousSibling);
}
var name = target.value.trim();
// if(code === KEYS.ENTER || code === KEYS.COMMA) {
if(code === KEYS.ENTER) {
target.blur();
addTag(name);
if(timer) clearTimeout(timer);
timer = setTimeout(function() { target.focus(); }, 10 );
}
else if(code === KEYS.BACK) {
if(e.target.value === '' && !isPressed) {
isPressed = true;
removeTag();
}
}
}
}
function backHandler(e) {
isPressed = false;
}
function addTag(name) {
// delete comma if comma exists
name = name.toString().replace(/,/g, '').trim();
if(name === '') return field.value = '';
if(~tagsArray.indexOf(name)) {
var exist = $$('.tag', wrap);
Array.prototype.forEach.call(exist, function(tag) {
if(tag.firstChild.textContent === name) {
addClass('tag--exists', tag);
if(transitionEnd) {
oneListener(tag, transitionEnd, function() {
removeClass('tag--exists', tag);
});
} else {
removeClass('tag--exists', tag);
}
}
});
return field.value = '';
}
var tag = createTag(name);
wrap.insertBefore(tag, field);
tagsArray.push(name);
field.value = '';
el.value += (el.value === '') ? name : ',' + name;
}
function removeTag() {
if(tagsArray.length === 0) return;
var tags = $$('.tag', wrap);
var tag = tags[tags.length - 1];
if( ! hasClass('tag--marked', tag) ) {
addClass('tag--marked', tag);
return;
}
tagsArray.pop();
wrap.removeChild(tag);
el.value = tagsArray.join(',');
}
init();
/* Public API */
this.getTags = function() {
return tagsArray;
}
this.clearTags = function() {
if(!el.instance) return;
tagsArray.length = 0;
el.value = '';
wrap.innerHTML = '';
wrap.appendChild(field);
}
this.addTags = function(name) {
if(!el.instance) return;
if(Array.isArray(name)) {
for(var i = 0, len = name.length; i < len; i++) {
addTag(name[i])
}
} else {
addTag(name);
}
return tagsArray;
}
this.destroy = function() {
if(!el.instance) return;
wrap.removeEventListener('click', btnRemove, false);
wrap.removeEventListener('keydown', keyHandler, false);
wrap.removeEventListener('keyup', keyHandler, false);
wrap.parentNode.removeChild(wrap);
tagsArray = null;
timer = null;
wrap = null;
field = null;
transitionEnd = null;
delete el.instance;
el.type = type;
}
}
window.Tags = Tags;
})();
// Use
var tags = new Tags('.tagged');
document.getElementById('get').addEventListener('click', function(e) {
e.preventDefault();
alert(tags.getTags());
});
document.getElementById('clear').addEventListener('click', function(e) {
e.preventDefault();
tags.clearTags();
});
document.getElementById('add').addEventListener('click', function(e) {
e.preventDefault();
tags.addTags('New');
});
document.getElementById('addArr').addEventListener('click', function(e) {
e.preventDefault();
tags.addTags(['Steam Machines', 'Nintendo Wii U', 'Shield Portable']);
});
document.getElementById('destroy').addEventListener('click', function(e) {
e.preventDefault();
if(this.textContent === 'destroy') {
tags.destroy();
this.textContent = 'reinit';
} else {
this.textContent = 'destroy';
tags = new Tags('.tagged');
}
});
//=== body styles
body {
padding: 20px;
font-family: "Segoe UI",Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;
}
form {
margin-bottom: 30px;
}
.form-control {
display: block;
width: 100%;
font-size: 14px;
height: 34px;
padding: 4px 8px;
margin-bottom: 15px;
}
//=== Tags
$enable-flex: true;
$tags-color: #fff;
$primary-color: #317CAF;
$marked-color: lighten($primary-color, 20%);
$exist-color: #EDB5A1;
$container-border: 1px solid #ccc;
$container-bg: transparent;
$container-line-height: 1.6;
$container-font-size: 14px;
$container-radius: 1px;
$container-min-height: 34px;
$container-padding: 2px 5px;
$tags-marging: 2px 6px 2px 0;
$tags-padding: 1px 20px 1px 8px;
$tags-weight: 400;
$tags-radius: 3px;
$btn-outline-color: #fff;
$spacer: 15px;
*, *:before, *:after {
box-sizing: border-box;
}
.tags-container {
@if($enable-flex) {
display: flex;
flex-flow: row wrap;
}
margin-bottom: $spacer;
width: 100%;
min-height: $container-min-height;
padding: $container-padding;
font-size: $container-font-size;
line-height: $container-line-height;
background-color: $container-bg;
border: $container-border;
border-radius: $container-radius;
overflow: hidden;
word-wrap: break-word;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
input.tag-input {
@if ($enable-flex) {
flex: 3;
}
@else {
display: inline-block;
vertical-align: middle;
}
border: 0;
outline: 0;
}
.tag {
@if ($enable-flex == false) {
display: inline-block;
vertical-align: middle;
}
position: relative;
margin: $tags-marging;
padding: $tags-padding;
font-size: inherit;
font-weight: $tags-weight;
text-align: center;
color: $tags-color;
background-color: $primary-color;
border-radius: $tags-radius;
transition: background-color .3s ease;
cursor: default;
&:first-child {
margin-left: 0;
}
&--marked {
background-color: $marked-color;
}
&--exists {
background-color: $exist-color;
animation: shake 1s linear;
}
&__name {
margin-right: 3px;
}
}
.tag__remove {
position: absolute;
right: 0;
bottom: 0;
width: 20px;
height: 100%;
padding: 0 5px;
font-size: 16px;
font-weight: 400;
transition: opacity .3s ease;
opacity: .5;
cursor: pointer;
border: 0;
background-color: transparent;
color: #fff;
line-height: 1;
&:hover {
opacity: 1;
}
&:focus {
outline: 5px auto $btn-outline-color;
}
}
@keyframes shake {
0%, 100% {
transform: translate3d(0, 0, 0);
}
10%, 30%, 50%, 70%, 90% {
transform: translate3d(-5px, 0, 0);
}
20%, 40%, 60%, 80% {
transform: translate3d(5px, 0, 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment