#### Basic

    <input name='basic' value='tag1, tag2 autofocus'>

    // The DOM element you wish to replace with Tagify
    var input = document.querySelector('input[name=basic]');

    // initialize Tagify on the above input node reference
    new Tagify(input)

#### Mix Text & Tags

Use `@` Symbol to add Programming language and `#` Symbol to add Companies. For Example - `@v` and `#a`

Hello, My name is [[Shaun]]. I am a [[{"id": 100, "text": "React", "title": "facebook"}]] Developer. I am currently working in [[{"value":"Microsoft"}]]

    <textarea name='mix'>Hello, My name is [[Shaun]]. I am a [[{"id": 100, "text": "React", "title": "facebook"}]] Developer. I am currently working in [[{"value":"Microsoft"}]]</textarea>

    /**
    *
    * Mix Text & Tags
    *
    **/

    // Define two types of whitelists, each used for the dropdown suggestions menu,
    // depending on the prefix pattern typed (@/#). See settings below.
    var whitelist_1 = [
        { value: 100, text: 'React', title: 'facebook' },
        { value: 200, text: 'Vue', title: 'Kelly Young' },
        { value: 300, text: 'Angular', title: 'Google' },
        { value: 400, text: 'Svelte', title: 'Rich Harris' },
        { value: 500, text: 'NextJS', title: 'Vercel' },
        { value: 600, text: 'Python', title: 'Guido van Rossum' },
        { value: 700, text: 'PHP', title: 'Rasmus Lerdorf' },
        { value: 800, text: 'Strapi', title: 'Open Source' },
        { value: 900, text: 'Wordpress', title: 'Automatic' },
        { value: 1000, text: 'Typescript', title: "Microsoft" }
        ]

    // Second whitelist, which is shown only when starting to type "#".
    // Thiw whitelist is the most simple one possible.
    var whitelist_2 = ['Microsoft', 'Apple', 'Google', 'Samsung', 'Esty', 'Github', 'Amazon', 'Ebay', 'FedEx', 'Facebook'];

    // initialize Tagify
    var input = document.querySelector('[name=mix]'),
        // init Tagify script on the above inputs
        mixMatch = new Tagify(input, {
        //  mixTagsInterpolator: ["{{", "}}"],
            mode: 'mix',  // <--  Enable mixed-content
            pattern: /@|#/,  // <--  Text starting with @ or # (if single, String can be used here)
            tagTextProp: 'text',  // <-- the default property (from whitelist item) for the text to be rendered in a tag element.
            // Array for initial interpolation, which allows only these tags to be used
            whitelist: whitelist_1.concat(whitelist_2).map(function(item){
                return typeof item == 'string' ? {value:item} : item
            }),
            dropdown : {
                enabled: 1,
                position: 'text', // <-- render the suggestions list next to the typed text ("caret")
                mapValueTo: 'text', // <-- similar to above "tagTextProp" setting, but for the dropdown items
                highlightFirst: true  // automatically highlights first sugegstion item in the dropdown
            },
            callbacks: {
                add: console.log,  // callback when adding a tag
                remove: console.log   // callback when removing a tag
            }
        })

    // A good place to pull server suggestion list accoring to the prefix/value
    mixMatch.on('input', function(e){
        var prefix = e.detail.prefix;

        // first, clean the whitlist array, because the below code, while not, might be async,
        // therefore it should be up to you to decide WHEN to render the suggestions dropdown
        // tagify.settings.whitelist.length = 0;

        if( prefix ){
            if( prefix == '@' )
                mixMatch.whitelist = whitelist_1;

            if( prefix == '#' )
                mixMatch.whitelist = whitelist_2;

            if( e.detail.value.length > 1 )
                mixMatch.dropdown.show(e.detail.value);
        }

        console.log( mixMatch.value )
        console.log('mix-mode "input" event value: ', e.detail)
    })

    mixMatch.on('add', function(e){
        console.log(e)
    })

#### Suggestions List

Search Languages with there `First Capital Letter` For Example: `JSON` with J

    <input name='tags3' value='[{"value":"React"}, {"value":"Angular"}]' pattern='^[A-Za-z_✲ ]{1,15}$'>

    /**
    *
    * Render Suggestion
    *
    **/

    var input = document.querySelector('input[name=tags3]'),
    tagify = new Tagify(input, {
        pattern             : /^.{0,20}$/,  // Validate typed tag(s) by Regex. Here maximum chars length is defined as "20"
        delimiters          : ",| ",        // add new tags when a comma or a space character is entered
        keepInvalidTags     : true,         // do not remove invalid tags (but keep them marked as invalid)
        editTags            : {
            clicks: 2,              // single click to edit a tag
            keepInvalid: false      // if after editing, tag is invalid, auto-revert
        },
        maxTags             : 6,
        blacklist           : ["foo", "bar", "baz"],
        whitelist           : ["React","Angular","TypeScript","Vue","Python","Wordpress","Svelte","PHP","Javascript","HTML","CSS","JSON","XML","Markdown","Go","C++","C#","Java","Docker","Perl","Ruby","Rust","NodeJS","SQL","SQLite","MonogDB","MYSQL","Swift","SCSS","SASS"],
        transformTag        : transformTag,
        backspace           : "edit",
        placeholder         : "Type something",
        dropdown : {
            enabled: 1,            // show suggestion after 1 typed character
            fuzzySearch: false,    // match only suggestions that starts with the typed characters
            position: 'text',      // position suggestions list next to typed text
            caseSensitive: true,   // allow adding duplicate items if their case is different
        },
        templates: {
            dropdownItemNoMatch: function(data) {
                return `<div class='${this.settings.classNames.dropdownItem}' tabindex="0" role="option">
                    No suggestion found for: <strong>${data.value}</strong>
                </div>`
            }
        }
    })

    // generate a random color (in HSL format, which I like to use)
    function getRandomColor(){
    function rand(min, max) {
        return min + Math.random() * (max - min);
    }

    var h = rand(1, 360)|0,
        s = rand(40, 70)|0,
        l = rand(65, 72)|0;

    return 'hsl(' + h + ',' + s + '%,' + l + '%)';
    }

    function transformTag( tagData ){
    tagData.color = getRandomColor();
    tagData.style = "--tag-bg:" + tagData.color;

    if( tagData.value.toLowerCase() == 'shit' )
        tagData.value = 's✲✲t'
    }

    tagify.on('add', function(e){
    console.log(e.detail)
    })

    tagify.on('invalid', function(e){
    console.log(e, e.detail);
    })

    var clickDebounce;

    tagify.on('click', function(e){
    const {tag:tagElm, data:tagData} = e.detail;

    // a delay is needed to distinguish between regular click and double-click.
    // this allows enough time for a possible double-click, and noly fires if such
    // did not occur.
    clearTimeout(clickDebounce);
    clickDebounce = setTimeout(() => {
        tagData.color = getRandomColor();
        tagData.style = "--tag-bg:" + tagData.color;
        tagify.replaceTag(tagElm, tagData);
    }, 200);
    })

    tagify.on('dblclick', function(e){
    // when souble clicking, do not change the color of the tag
    clearTimeout(clickDebounce);
    })

#### Users List

    <input name='users-list-tags' value='abatisse2@nih.gov, Justinian Hattersley'>

    /**
    *
    * Users List
    *
    **/

    // https://www.mockaroo.com/

    var inputElm = document.querySelector('input[name=users-list-tags]');

    function tagTemplate(tagData){
        return `
            <tag title="${tagData.email}"
                    contenteditable='false'
                    spellcheck='false'
                    tabIndex="-1"
                    class="tagify__tag ${tagData.class ? tagData.class : ""}"
                    ${this.getAttributes(tagData)}>
                <x title='' class='tagify__tag__removeBtn' role='button' aria-label='remove tag'></x>
                <div>
                    <div class='tagify__tag__avatar-wrap'>
                        <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                    </div>
                    <span class='tagify__tag-text'>${tagData.name}</span>
                </div>
            </tag>
        `
    }

    function suggestionItemTemplate(tagData){
        return `
            <div ${this.getAttributes(tagData)}
                class='tagify__dropdown__item ${tagData.class ? tagData.class : ""}'
                tabindex="0"
                role="option">
                ${ tagData.avatar ? `
                <div class='tagify__dropdown__item__avatar-wrap'>
                    <img onerror="this.style.visibility='hidden'" src="${tagData.avatar}">
                </div>` : ''
                }
                <strong>${tagData.name}</strong>
                <span>${tagData.email}</span>
            </div>
        `
    }

    // initialize Tagify on the above input node reference
    var usrList = new Tagify(inputElm, {
        tagTextProp: 'name', // very important since a custom template is used with this property as text
        enforceWhitelist: true,
        skipInvalid: true, // do not remporarily add invalid tags
        dropdown: {
            closeOnSelect: false,
            enabled: 0,
            classname: 'users-list',
            searchKeys: ['name', 'email']  // very important to set by which keys to search for suggesttions when typing
        },
        templates: {
            tag: tagTemplate,
            dropdownItem: suggestionItemTemplate
        },
        whitelist: [
            {
                "value": 1,
                "name": "Justinian Hattersley",
                "avatar": "https://i.pravatar.cc/80?img=1",
                "email": "jhattersley0@ucsd.edu"
            },
            {
                "value": 2,
                "name": "Antons Esson",
                "avatar": "https://i.pravatar.cc/80?img=2",
                "email": "aesson1@ning.com"
            },
            {
                "value": 3,
                "name": "Ardeen Batisse",
                "avatar": "https://i.pravatar.cc/80?img=3",
                "email": "abatisse2@nih.gov"
            },
            {
                "value": 4,
                "name": "Graeme Yellowley",
                "avatar": "https://i.pravatar.cc/80?img=4",
                "email": "gyellowley3@behance.net"
            },
            {
                "value": 5,
                "name": "Dido Wilford",
                "avatar": "https://i.pravatar.cc/80?img=5",
                "email": "dwilford4@jugem.jp"
            },
            {
                "value": 6,
                "name": "Celesta Orwin",
                "avatar": "https://i.pravatar.cc/80?img=6",
                "email": "corwin5@meetup.com"
            },
            {
                "value": 7,
                "name": "Sally Main",
                "avatar": "https://i.pravatar.cc/80?img=7",
                "email": "smain6@techcrunch.com"
            },
            {
                "value": 8,
                "name": "Grethel Haysman",
                "avatar": "https://i.pravatar.cc/80?img=8",
                "email": "ghaysman7@mashable.com"
            },
            {
                "value": 9,
                "name": "Marvin Mandrake",
                "avatar": "https://i.pravatar.cc/80?img=9",
                "email": "mmandrake8@sourceforge.net"
            },
            {
                "value": 10,
                "name": "Corrie Tidey",
                "avatar": "https://i.pravatar.cc/80?img=10",
                "email": "ctidey9@youtube.com"
            },
            {
                "value": 11,
                "name": "foo",
                "avatar": "https://i.pravatar.cc/80?img=11",
                "email": "foo@bar.com"
            },
            {
                "value": 12,
                "name": "foo",
                "avatar": "https://i.pravatar.cc/80?img=12",
                "email": "foo.aaa@foo.com"
            },
        ]
    })

    usrList.on('dropdown:show dropdown:updated', onDropdownShow)
    usrList.on('dropdown:select', onSelectSuggestion)

    var addAllSuggestionsElm;

    function onDropdownShow(e){
        var dropdownContentElm = e.detail.tagify.DOM.dropdown.content;

        if( usrList.suggestedListItems.length > 1 ){
            addAllSuggestionsElm = getAddAllSuggestionsElm();

            // insert "addAllSuggestionsElm" as the first element in the suggestions list
            dropdownContentElm.insertBefore(addAllSuggestionsElm, dropdownContentElm.firstChild)
        }
    }

    function onSelectSuggestion(e){
        if( e.detail.elm == addAllSuggestionsElm )
            usrList.dropdown.selectAll();
    }

    // create a "add all" custom suggestion element every time the dropdown changes
    function getAddAllSuggestionsElm(){
        // suggestions items should be based on "dropdownItem" template
        return usrList.parseTemplate('dropdownItem', [{
                class: "addAll",
                name: "Add all",
                email: usrList.whitelist.reduce(function(remainingSuggestions, item){
                    return usrList.isTagDuplicate(item.value) ? remainingSuggestions : remainingSuggestions + 1
                }, 0) + " Members"
            }]
            )
    }

#### Single Value Select

    <input name='tags-select-mode' class='selectMode' placeholder="Please select" />

    /**
    *
    *  Single Value Select
    *
    */

    var input = document.querySelector('input[name=tags-select-mode]'),
        tagify = new Tagify(input, {
            enforceWhitelist: true,
            mode : "select",
            whitelist: ["first option", "second option", "third option"],
            blacklist: ['foo', 'bar'],
        })

    // bind events
    tagify.on('add', onAddTag)
    tagify.DOM.input.addEventListener('focus', onSelectFocus)

    function onAddTag(e){
        console.log(e.detail)
    }

    function onSelectFocus(e){
        console.log(e)
    }