“Tagger” Plugin for jQuery

The Problem

Tags are really cool but can be clumsy to edit as comma-seperated lists in a tiny text box:

In the example above, it is difficult to see many tags at once. The user must also be very careful of where the commas are placed, as they will be interepreted as tag separators.

The Solution

Each tag should be wrapped in its own container to clearly distinguish it as a separate entity to the user. This helps eliminate confusion while allowing for editing at the tag level (as opposed to editing at the tag-list level).

The tag input box should have a generous width, about as long as a few average-size tags. The tag entry box should also grow vertically as more tags are added so that all tags can be seen.

Demo

The Tagger plugin makes it easy to convert any standard text-input box into an easy-to-use Tagger box. Just click and type as usual, but when you press “,” or [Enter], the current text is wrapped in its own little box, making it easy to see each tag.

You can also click an existing tag to edit it, or clear it by deleting the text.

moviesfilmstheatrical presentations

Usage

01 // Simple
02 $('input[name=tags]').tagger();
03
04 // With options
05 $('input[name=tags]').tagger({
06     'class':        'tagger',                   // specify a custom class name for the tagger container
07     inputAutosize:  true,                       // if true, the size of the fake input box will grow with the text (recommended)
08     beforeAdd:      function(tag, tagger) {},   // if the callback function returns false, the tag will not be added
09     afterAdd:       function(tag, tagger) {},   // called after a tag has been added
10     beforeEdit:     function(tag, tagger) {},   // if the callback function returns false, the tag will not be editable
11     afterEdit:      function(tag, tagger) {},   // called after the tag has been edited
12     beforeDelete:   function(tag, tagger) {},   // if the callback function returns false, the tag will not be deleted
13     beforeDelete:   function(tag, tagger) {}    // called after the tag has been deleted
14 });

Source

001 /*
002 Tagger - Converts any text input box into an easy-to-edit multiple-tag interface.
003 */
004
005 (function($){
006     var trim = function(str)
007     {
008         return str.replace(/^\s+|\s+$/g, '');
009     };
010     
011     var emptyFunction = function(){};
012     
013     var Tagger = function(element, options)
014     {
015         var obj = this;
016         var element = $(element);
017         var container = $('<div></div>');
018         var input = $('<input type="text"/>');
019         
020         var defaults = {
021             'class':        'tagger',
022             inputAutosize:  true,
023             beforeAdd:      emptyFunction,
024             afterAdd:       emptyFunction,
025             beforeEdit:     emptyFunction,
026             afterEdit:      emptyFunction,
027             beforeDelete:   emptyFunction,
028             afterDelete:    emptyFunction
029         };
030         
031         var config = $.extend(defaults, options || {});
032         
033         this.getContainer = function()
034         {
035             return container;
036         };
037         
038         this.getConfig = function()
039         {
040             return config;
041         };
042         
043         this.addTag = function(tag)
044         {
045             if (false === config.beforeAdd(tag, obj)) return obj;
046             
047             if (tag.getContainer !== undefined) {
048                 input.before(tag.getContainer());
049             }
050             
051             obj.updateRealInput();
052             config.afterAdd(tag, obj);
053             
054             return obj;
055         };
056         
057         if (config['class']) container.addClass(config['class']);
058         element.hide(0);
059         element.after(container);
060         container.append(input);
061         
062         container.click(function()
063         {
064             input.focus();
065         });
066         
067         this.insertCurrentInput = function()
068         {
069             var val = input.val();
070             
071             if (val) {
072                 var tag = new Tag(obj);
073                 tag.setTag(val);
074                 obj.addTag(tag);
075             }
076             
077             input.val('');
078         };
079         
080         input.keydown(function(event)
081         {
082             switch (event.keyCode) {
083                 // comma, enter
084                 case 188:
085                 case 13:
086                     obj.insertCurrentInput();
087                     event.preventDefault();
088                     break;
089             }
090         });
091         
092         input.keypress(function(event)
093         {
094             // Detect backspace
095             if (event.keyCode != 8) return;
096             
097             if (!input.val()) {
098                 // "click" last item
099                 container.find('span.tag:last').click();
100                 event.preventDefault();
101             }
102         });
103         
104         input.blur(obj.insertCurrentInput);
105         
106         
107         this.autogrow = function()
108         {
109             if (true != config.inputAutosize) return;
110             var size = input.val().length + 1;
111             input.attr('size', size);
112         };
113         
114         this.getInput = function()
115         {
116             return input;
117         };
118         
119         this.createTag = function(tagName)
120         {
121             var tag = new Tag(obj);
122             tag.setTag(tagName);
123             obj.addTag(tag);
124         };
125         
126         this.updateRealInput = function()
127         {
128             var allTags = [];
129             container.find('span.tag').each(function()
130             {
131                 allTags.push($(this).text());
132             });
133             var tags = allTags.join(', ');
134             element.val(tags);
135         };
136         
137         input.change(this.autogrow);
138         input.keyup(this.autogrow);
139         
140         // Handle current value
141         var tagString = element.val();
142         if (tagString) {
143             var tags = tagString.split(',');
144             $.each(tags, function(index, tagName)
145             {
146                 obj.createTag(tagName);
147             });
148         }
149     };
150     
151     var Tag = function(tagger)
152     {
153         var obj = this;
154         var tagger = tagger;
155         var container = $('<span></span>');
156         container.addClass('tag');
157         
158         this.getContainer = function()
159         {
160             return container;
161         };
162         
163         this.remove = function()
164         {
165             return container.remove();
166         };
167         
168         this.setTag = function(newTag)
169         {
170             tag = trim(newTag);
171             if (!tag) {
172                 if (false === tagger.getConfig().beforeDelete(obj, tagger)) return;
173                 
174                 var previous = container.prev('span.tag');
175                 if (previous.length > 0) {
176                     previous.click();
177                 } else {
178                     tagger.getInput().show(0).focus();
179                 }
180                 container.remove();
181                 tagger.getConfig().afterDelete(obj, tagger);
182                 
183                 return;
184             }
185             tagger.getInput().show(0).focus();
186             container.text(tag);
187             
188             tagger.updateRealInput();
189         };
190         
191         this.edit = function(event)
192         {
193             if (false === tagger.getConfig().beforeEdit(obj, tagger)) return;
194             
195             tagger.getInput().hide(0);
196             var input = $('<input type="text"/>');
197             
198             var doneEditing = function()
199             {
200                 obj.setTag(input.val());
201                 input.remove();
202                 container.removeClass('editing');
203             };
204             
205             input.css({width: container.outerWidth() + 'px'});
206             input.val(container.text());
207             input.blur(doneEditing);
208             input.keydown(function(event)
209             {
210                 switch (event.keyCode) {
211                     // comma, enter
212                     case 188:
213                     case 13:
214                         doneEditing();
215                         event.preventDefault();
216                         break;
217                 }
218             });
219             
220             input.keypress(function(event)
221             {
222                 // Backspace
223                 if (event.keyCode != 8) return;
224                 
225                 if (!input.val()) {
226                     doneEditing();
227                     event.preventDefault();
228                 }
229             });
230             
231             input.click(function(event)
232             {
233                 event.stopPropagation();
234             });
235             
236             container.html(input);
237             container.addClass('editing');
238             input.focus();
239             
240             event.stopPropagation();
241             
242             tagger.getConfig().afterEdit(obj, tagger);
243         };
244         
245         container.click(this.edit);
246     };
247     
248     $.fn.extend({
249         tagger:         function(options)
250         {
251             return this.each(function()
252             {
253                 if ($(this).data('tagger')) return;
254                 
255                 var tagger = new Tagger(this, options);
256                 $(this).data('tagger', tagger);
257             });
258         }
259     });
260 })(jQuery);
Advertisements

39 comments on ““Tagger” Plugin for jQuery

  1. I approximating this post, enjoyed this one express gratitude you for putting cheery. “He removes the furthermost prettify of friendship, who takes gone as of it respect.” by Cicero.

  2. It’s a shame you don’t have a donate button! I’d definitely donate to this excellent blog! I suppose for now i’ll settle for book-marking and adding your RSS feed to my Google account. I look forward to fresh updates and will share this blog with my Facebook group. Chat soon!

  3. I was curious if you ever thought of changing the page layout of your website? Its very well written; I love what youve got to say. But maybe you could a little more in the way of content so people could connect with it better. Youve got an awful lot of text for only having one or 2 pictures. Maybe you could space it out better?

  4. I conceive this website has some real superb information for everyone :D. “Believe those who are seeking the truth doubt those who find it.” by Andre Gide.

  5. Hello just thought i would tell you something.. This is twice now i’ve landed on your blog in the last 2 days searching for completely unrelated things. Spooky or what?

  6. Im no expert, but I consider you just crafted a very good point point. You definitely know what youre speaking about, and I can definitely get behind that. Thanks for being so upfront and so truthful.

  7. I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work.

  8. Hi, I just found your blog via google. Your post is truly applicable to my life at this moment, and I’m really delighted I found your website.

  9. Hey! I know this is kinda off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form? I’m using the same blog platform as yours and I’m having difficulty finding one? Thanks a lot!

  10. Hello – I must say, I’m impressed with your site. I had no trouble navigating through all the tabs and information was very easy to access. I found what I wanted in no time at all. Pretty awesome. Would appreciate it if you add forums or something, it would be a perfect way for your clients to interact. Great job

  11. You lost me, buddy. I mean, I imagine I get what youre indicating. I recognize what you are saying, but you just appear to have forgotten about that there are some other men and women inside the world who look at this issue for what it genuinely is and might not agree with you. You might be turning away a lot of men and women who may have been lovers of your web log.

  12. Hello – I must say, I’m impressed with your site. I had no trouble navigating through all the tabs and information was very easy to access. I found what I wanted in no time at all. Pretty awesome. Would appreciate it if you add forums or something, it would be a perfect way for your clients to interact. Great job

  13. Information and facts are comparatively introduced, every little thing since it ought to be. Shots of excellent outstanding, great origins featured. In a concept: superb! Believe definitely will bring up to date the web site, chcetnie continue reading within your content articles.

  14. I’ve been recently meditating on the similar point personally lately. Delighted to see another person on the same wavelength! Nice article.

  15. I cling on to listening to the news broadcast lecture about receiving boundless online grant applications so I have been looking around for the finest site to get one. Could you advise me please, where could i find some?

  16. We’re a group of volunteers and opening a new scheme in our community. Your site provided us with valuable info to work on. You’ve done an impressive job and our whole community will be thankful to you.

  17. Pretty nice post. I just stumbled upon your weblog and wanted to say that I have truly enjoyed browsing your blog posts. In any case I will be subscribing to your rss feed and I hope you write again soon!

  18. I have been exploring for a bit for any high quality articles or blog posts on this kind of area . Exploring in Yahoo I at last stumbled upon this site. Reading this info So i am happy to convey that I have an incredibly good uncanny feeling I discovered exactly what I needed. I most certainly will make certain to do not forget this website and give it a glance regularly.

  19. Enjoyed the post, I have a question, I’m looking for an alternative medicine doctor because I need the best, I wanted reviews or feedback on Hillsborough Rehab and Pain Management 719 Route 206 Suite 102 Hillsborough, NJ 08844 (908) 428-9565

  20. Howdy! This blog post could not be written much better! Going through this post reminds me of my previous roommate! He continually kept preaching about this. I’ll send this post to him. Fairly certain he’s going to have a great read. I appreciate you for sharing!

  21. My partner and I stumbled over here by a different web
    page and thought I might check things out.

    I like what I see so i am just following you. Look
    forward to finding out about your web page again.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s