) * Supports whitelist/blacklist * Supports Templates for: component wrapper , tag items , suggestion list & suggestion items Shows suggestions selectbox (flexiable settings & styling) at full (component) width or next to* the typed texted (caret) * Allows setting suggestions' aliases for easier fuzzy-searching * Auto-suggest input as-you-type with ability to auto-complete * Can paste in multiple values: tag 1, tag 2, tag 3 or even newline-separated tags * Tags can be created by Regex delimiter or by pressing the "Enter" key / focusing of the input * Validate tags by Regex pattern * Tags may be editable (double-click) * ARIA accessibility support(Component too generic for any meaningful ARIA) * Supports read-only mode to the whole componenet or per-tag * Each tag can have any properties desired (class, data-whatever, readonly...) * Automatically disallow duplicate tags (vis "settings" object) * Has built-in CSS loader, if needed (Ex. AJAX whitelist pulling) * Tags can be trimmed via hellip by giving max-width to the tag element in your CSS * Easily change direction to RTL (via the SCSS file) * Internet Explorer - A polyfill script should be used: tagify.polyfills.min.js (in /dist) * Many useful custom events * Original input/textarea element values kept in sync with Tagify
Building the project Simply run gulp in your terminal, from the project's path (Gulp should be installed first).Source files are this path:
/src/Output files, which are automatically generated using Gulp, are in:
/dist/The rest of the files are most likely irrelevant.
Adding tags dynamically `javascript var tagify = new Tagify(...);tagify.addTags(["banana", "orange", "apple"])
// or add tags with pre-defined propeties
tagify.addTags([{value:"banana", color:"yellow"}, {value:"apple", color:"red"}, {value:"watermelon", color:"green"}])
`
Output value There are two possible ways to get the value of the tags:1. Access the tagify's instance's
value prop: tagify.value (Array of tags) 2. Access the original input's value: inputElm.value (Stringified Array of tags)The most common way is to simply listen to the
change event on the original input
`javascript var inputElm = document.querySelector, tagify = new Tagify (inputElm);inputElm.addEventListener('change', onChange)
function onChange(e){ // outputs a String console.log(e.target.value) }
`
$3 Default format is a JSON string:
'[{"value":"cat"}, {"value":"dog"}]'I recommend keeping this because some situations might have values such as addresses (tags contain commas):
'[{"value":"Apt. 2A, Jacksonville, FL 39404"}, {"value":"Forrest Ray, 191-103 Integer Rd., Corona New Mexico"}]'Another example for complex tags state might be disabled tags, or ones with custom identifier class :(tags can be clicked, so delevopers can choose to use this to disable/enable tags)
'[{"value":"cat", "disabled":true}, {"value":"dog"}, {"value":"bird", "class":"color-green"}]'To change the format, assuming your tags have no commas and are fairly simple:
`js var tagify = new Tagify(inputElm, { originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(',') })`Output:
"cat,dog"
Ajax whitelist Dynamically-loaded suggestions list (whitelist ) from the server (as the user types) is a frequent need to many.Tagify comes with its own loading animation, which is a very lightweight CSS-only code, and the loading state is controlled by the method
tagify.loading which accepts true or false as arguments.Below is a basic example using the
fetch API. I advise to abort the last request on any input before starting a new request. Example:
`javascript var input = document.querySelector('input'), tagify = new Tagify(input, {whitelist:[]}), controller; // for aborting the call// listen to any keystrokes which modify tagify's input tagify.on('input', onInput)
function onInput( e ){ var value = e.detail.value; tagify.settings.whitelist.length = 0; // reset the whitelist
// https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort controller && controller.abort(); controller = new AbortController();
// show loading animation and hide the suggestions dropdown tagify.loading(true).dropdown.hide.call(tagify)
fetch('http://get_suggestions.com?value=' + value, {signal:controller.signal}) .then(RES => RES.json()) .then(function(whitelist){ // update inwhitelist Array in-place tagify.settings.whitelist.splice(0, whitelist.length, ...whitelist) tagify.loading(false).dropdown.show.call(tagify, value); // render the suggestions dropdown }) }
`
Edit tags Tags which aren't read-only can be edited by double-clicking them (by default) or by changing the editTags setting to 1, making tags editable by single-clicking them.The value is saved on
blur or by pressing enter key. Pressing Escape will revert the change trigger blur.ctrl z will revert the change if an edited tag was marked as not valid (perhaps duplicate or blacklisted)To prevent all tags from being allowed to be editable, set the
editTags setting to false (or null). To do the same but for specific tag(s), set those tags' data with editable property set to false:
`html`
Drag & Sort To be able to sort tags by draging, a 3rd-party script is needed.
I have made a very simple drag & drop (~
11kb unminified ) script which uses HTML5 native API and it is available to download via NPM or Github but any other drag & drop script may possibly work. I could not find in the whole internet a decent lightweight script.
$3
`js var tagify = new Tagify(inputElement)// bind "DragSort" to Tagify's main element and tell // it that all the items with the below "selector" are "draggable" var dragsort = new DragSort(tagify.DOM.scope, { selector: '.'+tagify.settings.classNames.tag, callbacks: { dragEnd: onDragEnd } })
// must update Tagify's value according to the re-ordered nodes in the DOM function onDragEnd(elm){ tagify.updateValueByDOMTags() }
`
DOM Templates It's possible to control the templates for some of the HTML elements tagify is using by modifying the settings.templates Object with your own custom functions which must return an HTML string .Available templates are:
wrapper, tag, dropdown, dropdownItem and the optional dropdownItemNoMatch which is a special template for rendering a suggestion item (in the dropdown list) only if there were no matches found for the typed input.View templates
Suggestions selectbox The suggestions selectbox is shown is a whitelist Array of Strings or Objects was passed in the settings when the Tagify instance was created. Suggestions list will only be rendered if there are at least two matching suggestions (case-insensitive).The selectbox dropdown will be appended to the document's
element and will be rendered by default in a position below (bottom of) the Tagify element. Using the keyboard arrows up/down will highlight an option from the list, and hitting the Enter key to select.It is possible to tweak the selectbox dropdown via 2 settings:
-
enabled - this is a numeral value which tells Tagify when to show the suggestions dropdown, when a minimum of N characters were typed. - maxItems - Limits the number of items the suggestions selectbox will render
`javascript var input = document.querySelector('input'), tagify = new Tagify(input, { whitelist : ['aaa', 'aaab', 'aaabb', 'aaabc', 'aaabd', 'aaabe', 'aaac', 'aaacc'], dropdown : { classname : "color-blue", enabled : 0, // show the dropdown immediately on focus maxItems : 5, position : "text", // place the dropdown near the typed text closeOnSelect : false, // keep the dropdown open after selecting a suggestion highlightFirst: true } });`
Will render
`html aaab
aaabb
aaabc
aaabd
aaabe
`By default searching the suggestions is using fuzzy-search (see settings ).
If you wish to assign alias to items (in your suggestion list), add the
searchBy property to whitelist items you wish to have an alias for.In the below example, typing a part of a string which is included in the
searchBy property, for example land midd" - the suggested item which match the value "Israel" will be rendered in the suggestions (dropdown) list.
$3
`javascript whitelist = [ ... { value:'Israel', code:'IL', searchBy:'holy land, desert, middle east' }, ... ]`Another handy setting is
dropdown.searchKeys which, like the above dropdown.searchBy setting, allows expanding the search of any typed terms to more than the value property of the whitelist items (if items are a Collection ).
$3
`javascript [ { value : 123456, nickname : "foo", email : "foo@mail.com" }, { value : 987654, nickname : "bar", email : "bar@mail.com" }, ...more.. ]`// setting to search in other keys:
`javascript { dropdown: { searchKeys: ["nickname", "email"] // fuzzy-search matching for those whitelist items' properties } }`
Mixed-Content > To use this feature it must be toggled - see settings .
When mixing text with tags, the original textarea (or input) element will have a value as follows:
[[cartman]] and [[kyle]] do not know [[Homer simpson]]
If the inital value of the textarea or input is formatted as the above example, tagify will try to automatically convert everything between
[[ & ]] to a tag, if tag exists in the whitelist , so make sure when the Tagify instance is initialized, that it has tags with the correct value property that match the same values that appear between [[ & ]].Applying the setting
dropdown.position:"text" is encouraged for mixed-content tags, because the suggestions list will be rendered right next to the caret location and not the the bottom of the Tagify componenet, which might look weird when there is already a lot of content at multiple lines.If a tag does not exists in the whitelist , it may be created by the user and all you should do is listen to the
add event and update your local/remote state.
Single-Value Similar to native
element, but allows typing text as value.
React A Tagify React component is exported from
react.tagify.js:> Note: You will need to inport Tagify's CSS also, either by javasceript or by SCSS
@import (which is preferable)`javascript import Tags from "@yaireo/tagify/dist/react.tagify" // React-wrapper file import "@yaireo/tagify/dist/tagify.css" // Tagify CSSconst App = () => { return ( tagifyRef={tagifyRef} // optional Ref object for the Tagify instance itself, to get access to inner-methods settings={settings} // tagify settings object value="a,b,c" {...tagifyProps} // dynamic props such as "loading", "showDropdown:'abc'", "value" onChange={e => (e.persist(), console.log("CHANGED:", e.target.value))} /> ) })
`To gain full acess to Tagify's inner methods, A custom
ref can be used:
$3
Angular TagifyComponent which will be used by your template as
Example:
` testing tagify wrapper (add)="onAdd($event)" (remove)="onRemove($event)"> clear add Tags
`TagifyService
> (The tagifyService is a singletone injected by angular, do not create a new instance of it) Remember to add
TagifyService to your module definition. Example:
`typescript import {Component, OnDestroy} from '@angular/core'; import {TagifyService} from '@yaireo/tagify';@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnDestroy {
constructor(private tagifyService: TagifyService) {} public settings = { blacklist: ['fucking', 'shit']};
onAdd(tagify) { console.log('added a tag', tagify); }
onRemove(tags) { console.log('removed a tag', tags); } clearTags() { this.tagifyService.removeAll(); } addTags() { this.tagifyService.addTags(['this', 'is', 'cool']); } ngOnDestroy() { this.tagifyService.destroy(); } }
`
jQuery version
jQuery.tagify.jsA jQuery wrapper verison is also available, but I advise not using it because it's basically the exact same as the "normal" script (non-jqueryfied) and all the jQuery's wrapper does is allowing to chain the event listeners for ('add', 'remove', 'invalid')
`javascript $('[name=tags]') .tagify() .on('add', function(e, tagData){ console.log('added', ...tagData) // data, index, and DOM node });`Accessing methods can be done via the
.data('tagify'):
`javascript $('[name=tags]').tagify(); // get tags from the server (ajax) and add them: $('[name=tags]').data('tagify').addTags('aaa, bbb, ccc')``
HTML input & textarea attributes The below list of attributes affect Tagify . These can also be set by Tagify settings Object manually, and not declerativly (via attributes).
Attribute | Example | Info ----------------- | ----------------------------------------------------- | --------------------pattern |
| Tag Regex pattern which tag input is validated by.placeholder | | This attribute's value will be used as a constant placeholder, which is visible unless something is being typed. readOnly | | No user-interaction (add/remove/edit) allowed. autofocus | | Automatically focus the the Tagify component when the component is loaded required | | Adds a required attribute to the Tagify wrapper element. Does nothing more.
FAQ List of questions & scenarios which might come up during development with Tagify:
$3 Tagify does not accept just any kind of data structure. If a tag data is represented as an
Object, it must contain a unique property value which Tagify uses to check if a tag already exists, among other things, so make sure it is present.Incorrect
`javascript [{ "id":1, "name":"foo bar" }]`Correct
`javascript [{ "id":1, "value": 1, "name":"foo bar" }]`
`javascript [{ "value":1, "name":"foo bar" }]`
`javascript [{ "value":"foo bar" }]`
`javascript // ad a simple array of Strings ["foo bar"]`
$3 In framework-less projects, the developer should save the state of the Tagify component (somewhere), and the question is:when should the state be saved? On every change made to Tagify's internal state (
tagify.value via the update() method).
`javascript var tagify = new Tagify(...)// listen to "change" events on the "original" input/textarea element tagify.DOM.originalInput.addEventListener('change', onTagsChange)
// This example uses async/await but you can use Promises, of course, if you prefer. async function onTagsChange(e){ const {name, value} = e.target // "imaginary" async function "saveToServer" should get the field's name & value await saveToServer(name, value) }
`If you are using React/Vue/Angular or any "modern" framework, then you already know how to attach "onChange" event listeners to your
/