Building jqLSAdmin – Part 2

In the previous post, I went through some of the infrastructure of this little project. The stuff-I-had-to-figure-out-first stuff that without it the rest of the project would have been doomed from the beginning. In this second post, I will go through some of the code I used to build it and talk about what the next steps for this will be. I’ll also post the link to the unminified source here, and invite comments and criticisms on ways to improve this.

One of the structural decisions I made for this code was that I wasn’t going to just inject a bunch of raw HTML with JS values injected in. Sure, I could just puke out HTML, or I could be smart and use jQuery’s innate ability to build it for me. Admittedly, I started doing it the old-fashioned way, and then slapped myself silly. There were a couple reasons for this self-slap. One, John Resig cries everytime you do $(“#dom_element”).html(“Value 1: “+value1+”
Value 2: “+value2+”
”);. I don’t want to make John cry. From all accounts, he’s a nice guy. Two, as this was going to be in a bookmarklet, I didn’t want to have to get into some REALLY ugly escaping of quotes and other such nonsense. Okay, I admit it, it was more the second reason than the first. I didn’t want to have to go through and put backslashes in a million places.

So, instead of doing this:

$(li).html('<input type="text" name="jqls_new_key" id="jqls_new_key" value="New Key" onfocus="if(this.value=='New Key') this.value=''" onblur="if(this.value=='')this.value='New Key'" style="color: #333; margin: 3px; width: 204px; float: left;" />');

I decided to do this:

var new_key = $('<input>');
new_key.attr({'type':'text', 'name':'jqls_new_key', 'value':'New Key', 'id': 'jqls_new_key'})
            .css({'color':'#333', 'margin':'3px', 'width':'204px', 'float':'left'})
            .focus(function() { if(this.value=='New Key') this.value='';})
            .blur(function() { if(this.value=='') this.value='New Key'; });

Which, as I hope you can see, is a lot more organized, structured, and ultimately easier to maintain. Plus, less Resig-tears. I’m using principles of chaining, using objects to pass multiple key/value pairs and using jQuery to create the new DOM element for me. The previous method is to DOM element creation what hemorrhagic bleeding is to fabric dye.

Now, you’ll notice that I do use the old hackery method of using focus and blur events to provide default text for the inputs. Again, while I am working in an HTML5 world, localStorage is much more widely supported than HTML5 form events like default values. At the point where this HTML5 feature, along with contenteditable is more widespread, then I will upgrade this tool to support them. (and it will likely make everything a hell of a lot easier and prettier)

Now, let’s talk about the actual function of the code. Here’s my basic pseudocode thought-process for this:

  1. Verify localStorage and jQuery are working
  2. Three functions are needed: a) initialize overlay and fieldset, b) populate fieldset with localStorage, c) bind actions to UI controls
  3. A) Set up overlay and inner div with DOM and CSS.
  4. Append inner div to overlay
  5. Set up fieldset, append to inner div
  6. Add a close link to close out the bookmarklet
  7. Call B) and C), in that order (order is important, as we need the UI elements there before we can bind actions to them)
  8. B) Set up a
      for population, both DOM and CSS
    • Make it look nice with an initial
    • for column headers
    • Iterate through the localStorage key/value pairs, populating
    • ’s as we go
    • Each key and value gets an edit (delta) symbol in its span
    • Each
    • has a delete symbol at the end of it
    • Append the input boxes for adding a new key/value pair to localStorage as the last
    • C) Delete handler deletes the
    • from the display and the localStorage key/value pair from the browser
    • Edit handler(Delta) replaces plain old text in the span with an input box pre-populated with that text
    • Replace delta with nabla.
    • Build a unique ID and apply it to the input field so that in case someone tries to edit multiple things at once, it’s going to save the right one
    • Store the original value of the key or value in a data atrribute on the input box for later comparison
    • Save handler(Nabla) checks the value of the input box against the stored data value
    • If they’re the same do nothing and replace the input box with the text so it’s almost like a toggle
    • Replace nabla with delta
    • If they’re different, enter into logic for whether it’s a key or a value being edited
    • Check class of container span to determine if it’s a key or not
    • If it’s a key, delete original key from localStorage and add new one with existing value
    • If it’s a value, update existing key/value pair with new value
    • Restore to text instead of input boxes, and nablas to deltas.
    • Save handler on new key/value boxes verifies you’re not submitting ‘New Key’ or ‘New Value’ into localStorage
    • Adds new key value pair to localStorage and refreshes by calling B).
    • All handlers end by calling B) again to repopulate fieldset.

Not quite a twelve step program, but a good outline. As I worked through this outline, there were a number of considerations that came up. I ended up having to use substring() a lot, as I had worked to give my ID’s some uniqueness by prepending the ID’s with jqls_

Also when I was using .text() to grab the contents of the ’s that held the key/value data, I would also end up with the delta/nabla signs, and had to edit those out using a method like the following:

data = data.substring(0,data.length - 1);

Nothing fancy, just stripping all but the last character. Of note is that the .text() method returns the HTML entities as a rendered character, not as its δ version. That made it a lot easier. Had it not done that, I surely would have gone the .data() route.

The only other “real” challenge that occurred when building this tool was deciding whether to use .append() or .appendTo() when building my DOM tree. I alternated, depending on the circumstance. .append() does allow you to do .append(elem1, elem2, elem3, …) which was useful in a few places. But, I also found that as I was using the method I describe above to construct my DOM elements in jQuery that the last link in my function chain was elegantly a .appendTo(parent_elem) call.

What’s Next for jqLSAdmin?

As for what’s next for this tool, I’ve already identified that more HTML5 features could (and should) be worked in. Specifically, I want to use contenteditable and the default values for form fields. Contenteditable is definitely a big one that would make this tool that much nicer. I could, and again probably should, add a link to clear the localStorage, as if you have a LOT of values in there, it would get tedious to have to click to delete them all.

A major issue that I know that I need to find a way to deal with is jQuery.noConflict(). I’ve put efforts in to detect if jQuery is already loaded on the page, but I haven’t done any efforts yet to determine if another framework on the page is using $. So, I know this is a big area for improvement. I am thinking that with the current architecture, I will need to pass $ as jQuery to the functions which initialize everything, and then continue to pass that back to the function which refreshes the localStorage. Or, use an internal alias like $j or $jQ or something inside of the functions and again pass the jQuery object to each function for use.

Depending on people’s response to the delta/nabla iconography, I could stick a small pencil gif in as base64 for the edit, and a teeny disk image in for saving. The code’s pretty big as it is, but again it all depends on user response.

I’m sure some of you may or may not agree with some of the CSS decisions I made. I’m not a master cascader, though I get by. I’m also not a design guy, I’m a code monkey. If anyone does have suggestions for making it more visually appealing, please let me know. I didn’t want to get into loading an external stylesheet as I wanted it to be all self-contained. Classes and ID declarations in CSS would have made this much easier to code.

This has been tested in a number of browsers. I’ve gotten reports that Chrome 11 isn’t playing nicely, but have yet to verify that. So far, I’ve played with it in Chrome 10, IE8, Safari5, FF4, and Opera11. Admittedly, IE gave me issues setting this as a bookmarklet, and Opera’s just stupid. I found that if I used a dynamic script loader to put the script into the page with all the browsers, it worked like a charm. And those that played nicely with bookmarklets allowed it to work as well.

I haven’t looked at this on mobile browsers at all, for obvious reasons. It’s hard to make bookmarklets on a 3″ screen. If anyone wants to take on the task of trying it, I’ll be grateful. I’ll also worry for your sanity a little bit.

Here’s a link to the unminified source. I definitely welcome any and all feedback that the community has.

Published 27 Mar 2011

Writing better code by building better JavaScript
Don Burks on Twitter