tagInput: A simple jQuery plugin for tag entry using Bootstrap 4

John Avis by | October 15, 2019 | Jquery/Javascript Web Development

For a website project I needed a way to enter multiple tags. I just wanted something simple that I could easily modify to suit my own needs, so I wrote my own.
img1096_taginput.jpg

For a recent website project I needed a way to enter multiple tags.

I tried a few open-source scripts but didn't find something that suited my needs. Some had browser compatibility, some had other issues, and some were huge!

I just wanted something simple that I could easily modify to suit my own needs, so I wrote my own.

My requirements:

• Progressive enhancement (if JavaScript is not available or not working, revert to a simple input text for data entry instead)
• Not allow duplicate tags
• Use Bootstrap 4.x classes
• Validate entered tags
• Autocomplete functionality using HTML 5 datalist
• Autocomplete from an AJAX data source

Demo

Update history

v1.0 - Initial release
v1.1 - Restricted minimum and maximum length of tags, handled tab key (can add tag, or if input text empty then moves to next field), added public method to add tags

//tagInput by John Avis Oct 16, 2019
//v1.1

(function ($, win) {
$.fn.tagInput = function () {
return this.each(function () {

//Single function used to add tags
function addTag(tagName) {
var cleanTagName = tagName.trim();

if (cleanTagName != '') {
//Input validation (this example allows minimum 2 and maximum 50 upper and lower case letters and numbers - change if required)
if (/^([a-zA-Z0-9]){2,50}$/.test(cleanTagName) === false) {
return '2-50 letters or numbers only';
}

//Check for duplicates (remove if not required)
var tags = el.val().split(",");
var rawTagName = rawTag(cleanTagName)
for (var i = 0; i < tags.length; i++) {
if (rawTag(tags[i]) === rawTagName) {
return 'Duplicate tag';
}
}

//check for maximum number of tags reached
if (tags.length >= 10) {
return 'Only 10 tags allowed';
}

//Add a tag to the list
el.val(el.val() + (el.val() !== '' ? ',' : '') + cleanTagName);
$('#' + id + '_form').before('<div class="float-left mr-1 mb-1"><span class="btn btn-secondary btn-sm">' + cleanTagName + ' <button type="button" class="btn badge badge-light" value="' + cleanTagName + '">×</a></span></div>');
return true;
}
}

function rawTag(tagName) {
//Compare tags by their raw values only by converting to lower case//
//You may also use this to remove any non alpha-numeric characters (if allowed) if some characters are ignored for comparisons
return tagName.toLowerCase();
}

//Global variables - the element (jquery object) itself, and the ID which is used as a prefix for dynamic elements
var el = $(this);
var id = el.attr('id');

//Add a reference to the object as data
el.data('tagInput', this);

//Hide the original input text
el.hide();

//Create the HTML elements for displaying the list of tags and for entering a new tag
el.after('<div id="' + id + '_list" class="clearfix border rounded pl-1 pt-1">' +
'<div id="' + id + '_form" class="float-left mb-1">' +
'<input id="' + id + '_input" type="text" autocomplete="off" class="form-control form-control-sm" maxlength="50" list="' + id + '_datalist" placeholder="Enter a tag" />' +
'<div id="' + id + '_error" class="text-danger small" style="display:none"></div>' +
'<button id="' + id + '_add" class="btn btn-outline-secondary sr-only" type="button">Add tag</button>' +
'</div>' +
'</div>' +
'<datalist id="' + id + '_datalist"></datalist>');

//Attempt to add any existing comma separated values from the input text (can change separator if required)
var tags = el.val().split(",");
el.val('');
for (var i = 0; i < tags.length; i++) {
addTag(tags[i]);
}

//Watch for blur and keyup
$('#' + id + '_input').bind('blur keyup', function (e) {

//Empty and hide any existing validation error
$('#' + id + '_error').html('').hide();

//Execute if blur event or enter key pressed (could also trap other keystrokes if required)
if (e.type === 'blur' || (e.type === 'keyup' && (e.keyCode === 13 || (e.shiftKey === false && e.keyCode === 9)))) {

//Empty and hide any existing validation error
$('#' + id + '_error').html('').hide();

//Attempt to add the tag and store the result
var result = addTag($(this).val());
if (result === true) {

//If was successful then clear the input text and autocomplete datalist
$(this).val('');
$('#' + id + '_datalist').html('');

//If enter or tab key was pressed then set focus back to input text
if (e.type === 'keyup' && (e.keyCode === 13 || (e.shiftKey === false && e.keyCode === 9))) {
$(this).focus();
}
}
else {
$('#' + id + '_error').html(result).show();
}
}
//Autocomplete datalist is only populated if at least one character has been entered (change if required)
else if ($(this).val().length < 1) {
//Less than 1 charcters so make sure the datalist is empty
$('#' + id + '_datalist').html('');
return;
//Execute if more than one character has been entered, will populate the datalist with matches
} else {
//Empty the datalist
$('#' + id + '_datalist').html('');

//Populate the datalist with dummy data (the first letter entered plus numbers 0 to 50)
//Replace with an AJAX request or remove if not required
for (i = 0; i < 50; i++) {
$('#' + id + '_datalist').append('<option>' + $(this).val().substring(0, 1) + i + '</option>');
}
}
});

//Prevent the enter key from submitting the form, and prevent tab key default action if input text has data
$('#' + id + '_input').bind('keydown', function (e) {
if (e.keyCode === 13 || (e.shiftKey === false && e.keyCode === 9 && $(this).val().trim() !== '')) {
e.preventDefault();
return false;
}
});

//Watch for delete tag button pressed
$('#' + id + '_list').on('click', '.badge', function () {

//Get current tags
var tags = el.val().split(',');

//Clear current values, ready to add all but the deleted tag
el.val('');

//Iterate through current tags and add all but the deleted tag
for (var i = 0; i < tags.length; i++) {
if (tags[i] != $(this).val()) {
el.val(el.val() + (el.val() !== '' ? ',' : '') + tags[i]);
}
}

//Remove the deleted tag element
$(this).parent().parent().remove();

//Set focus back to the input text
el.focus();
});

//A public method for adding extra tags
this.addTags = function (tagNames) {
var tags = tagNames.split(',');
for (var i = 0; i < tags.length; i++) {
addTag(tags[i]);
}
}
});
};
}(jQuery, window));

Related Posts

Computers & Internet Web Development Website Hosting

500 Internal Server Error after migrating from IIS 7.5 to IIS 10

by John Avis | November 4, 2019

As support ends for Microsoft Windows Server 2008 I have recently gone through migrating some websites to a new server running Windows Server 2016 and IIS 10 but some of the websites did not work.


ASP.NET Web Forms Web Development

ASP.NET bug with RadioButton GroupName in Repeater

by John Avis | September 1, 2019

I rediscovered a bug in ASP.NET that affects RadioButtons inside repeaters. Here is my solution to the problem.


ASP.NET Web Forms Bootstrap Web Development

More on Bootstrap 4 modals in ASP.NET Web Forms applications using UpdatePanels

by John Avis | April 9, 2019

In this, my third post on this topic, I go over the methods I am using now and the problems I have encountered and overcome.

Comments

There are no comments yet. Be the first to leave a comment!

Leave a Comment

Tags

About me

...random postings about web development and programming, Internet, computers, electronics and automotive topics.

Subscribe

Get the latest posts delivered to your inbox.