jQuery Object or Bust

We use jQuery in our web app. I’m starting to use more native JavaScript now that browsers are getting better at creating a consistent API for the native DOM and JavaScript features. But there’s still a lot of usefulness in jQuery, and sometimes when a function requires an input be a jQuery (DOM) object, we’ll need to check it and wrap it pass it to jQuery if it’s not already a jQuery object. (Alright kids, let’s see how many times can we say jQuery in a single paragraph!)

I’ve already written about this a couple times, and it keeps coming up because I keep trying to make it more efficient. Of course, the native document.getElementById(‘id’) and document.querySelectorAll(‘sel’) are usually the fastest way to get a DOM element, but if you still need that as a jQuery object, you gotta call jQuery anyway, so… Let’s try to speed that up, even if just a little bit.

Here’s the latest iteration of this (handy?) function:

function $$(el){
    var $el = el,
        id_prefix = /##|#:|id:/;
    // we only need to run this if el is not already a jQuery object
    if (!$el.jquery) {
        // special case for (faster?) handling of id
        // using a "special" id-only selector:
        //  "##", "#:", or "id:"
        // (this will tolerate crazy non-standard ids)
        // document.getElementyById("foo.bar/baz") ==
        // $$("##foo.bar/baz") == $$("#:foo.bar/baz") == $$("id:foo.bar/baz")
        if (el.search(id_prefix) === 0) {
            el = el.replace(id_prefix, "");
            $el = $(document.getElementById(el));
        }
        // NOW try the standard jQuery selector
        else {
            $el = $(el);
        }
    }
    return $el;
}

// here's how we use it:
// attaching cached object on $$ for fun
$$.emailInput = $$("input[type='email']");
// now we can use jQuery's methods on it
$$.emailInput.on("focus", function(){
    this.value = "";
});

That’s it. This should be faster if we pass a selector that is an id (with special syntax), and will tolerate silly non-standard id values that could contain other strings that would mean something else in the context of a CSS-type selector (like dots for class, colons for pseudo-selectors, etc.).

Advertisements

Object [Deep] Merge/Extend in POJS (Plain Ol’ JavaScript)

I know there are a lot of JavaScript libraries that include a method to merge/extend (we’ll call it merge from here on) plain objects, but what if: 1) you’re not using one of those (unlikely these days), or 2) you need to do some object merge magic before one of those handy libraries loads (very possible, and the reason why I wrote this function, which has probably been written thousands of times in the history of JavaScript, but oh well)?

Here’s a native JavaScript function to do just that…
https://gist.github.com/Error601/9a181a0b9f414b752c38

In this example, I’m declaring a named function while also adding it as a method to an “APP” namespace object (you know, for the web app we’re writing).

// make sure we've got our APP namespace object
var APP = APP || {};

(function( undefined ){

    function isObject( obj ){
        return Object.prototype.toString.call(obj) === "[object Object]";
    }

    if (!isObject(APP)) {
        APP = {}
    }

    APP.mergeObjects = function mergeObjects( /* [true ,] objects */ ){

        var args = arguments,
            arg1 = args[0],
            arg2 = args[1] || {},
            len  = args.length,
            deep = false,
            i, arg, output = {};


        // first argument will be output object
        if ( isObject(arg1) ){
            output = arg1;
        }
        // unless set to true
        else if ( arg1.toString() === "true" ){
            deep = true;
            output = arg2;
        }

        // stop here and return the output object
        // if argument length is 0 or 1, or
        // if we're trying to deep merge a single object
        if ( len <= 1 || (len === 2 && deep === true) ){
            return output;
        }

        function processObject( obj ){
            var prop;
            for ( prop in obj ){
                if ( obj.hasOwnProperty(prop) ){
                    if ( deep && isObject(obj[prop]) ){
                        output[prop] = mergeObjects( true, output[prop], obj[prop] );
                    }
                    else {
                        output[prop] = obj[prop];
                    }
                }
            }
        }

        // loop over additional arguments
        for ( i = 1; i < len; i++ ){
            arg = args[i];
            if ( isObject(arg) ) {
                processObject(arg);
            }
        }

        return output;

    };

})();

Giant function or crazy simple RegExp?

Working with some old code I ran across a 37-line JavaScript function that seems to just strip non-alphanumeric characters from a text <input>. It took a string as its argument and returned a sanitized string with underscores in place of the bogus characters. There was also a function to throw up a custom modal dialog telling the user their input was being ‘corrected’ before submission (but not giving them a chance to stop and change it if they’d like).

The string replace method was called 30 separate times, like:

value = value.replace(/[&]/,"_"); // x30 to catch 30 different illegal characters

…30 separate times:

value = value.replace(/[&]/, "_");
value = value.replace(/[%]/, "_");  
//etc.

That’ll get the job done, in a very verbose and tedious way… But… How about just chaining the .replace() method, like:

value = value.replace(/[&]/,"_").replace(/[%]/,"_");  //etc.

But even that’s a bit silly. I thought, “Why not just put all the characters into a SINGLE regex and do a global search for those characters and replace them all at once.” That makes sense, right?

value = value.replace(/[\`~!@#$%^&*()\-_=+\[\]{};:'"<>?,.\/\\|\s]/g, "_");

That smashes it down to a single line (down from 30). But… What if we don’t have all the characters we want to block accounted for there? Someone could try to sneak in a © or ¢. There’s got to be a way to only ALLOW the proper (alpha, number, and underscore) characters. Of course there is!

value = value.replace(/\W/g, "_");

Is that really all I needed? Alright then. 30 lines down to ONE super-simple RegExp replace and we’re good to go.

Quite silly.

Here are some resources that helped set all of this silliness straight:

Kind of:
http://stackoverflow.com/questions/3780696/javascript-string-replace-with-regex-to-strip-off-illegal-characters

Closer:
http://stackoverflow.com/questions/388996/regex-for-javascript-to-allow-only-alphanumeric

The last bit of info that did the trick (the “Character Classes” section):
http://www.javascriptkit.com/javatutors/re2.shtml yes, really, an ancient JavaScript Kit link

Get The Current Script’s Path (non-jQuery)

So, I use this snippet quite a lot, usually for loading a CSS file that’s in the same folder as the script file. I’ve used it with jQuery since it’s a very popular library (and we use it in the web app I work on), but this function can be performed without jQuery using the document.querySelectorAll(‘sel’) method.

function getScriptDir() {
    var scripts, src, path;
    scripts = document.querySelectorAll("script[src]");
    src = scripts[scripts.length-1].src;
    if (src.indexOf("/") !== -1) {
        path = src.split("/");
        path.splice(path.length - 1, 1);
        return path.join("/") + "/";
    }
    else {
        return "";
    }
}

This works because the script where this function resides doesn’t know about any other files that come after it (just be sure to fire it right away – DO NOT wait for the entire DOM to load).

Here are previous versions of this function…

The jQuery version is in this post:
https://error601.wordpress.com/2014/08/21/some-code-snippet-updates/

The “original” (included here for reference, but not recommended since it can’t handle src attributes without a slash, which is dumb):
https://error601.wordpress.com/2014/02/12/get-the-current-scripts-path/

Example: Get date format by class name and submit data via AJAX (with jQuery)

For this example, we’re going to leverage a function in a namespace (APP.utils) named (APP.utils.getValueByClass()) that I wrote in my last post (Set a value based on the presence of a class name (with jQuery)). We’re going to get the value of an input (a date), determine the date format by class name, and submit the day, month, and year to our web server via AJAX and display the response (text “success”) when done.

The HTML:

<!-- the 'us' class name indicates the date format, the placeholder directs the user -->
<input type="text" id="start" name="start" class="date us" placeholder="09/02/2014">
<p id="success"></p>

The JavaScript:

// this function gets us the day, month, and year values
APP.utils.getDateValues = function( date, format ){
    var parts, sep, d, m, y;
    // allow use of either "/" or "-" for separators    
    sep = (date.indexOf("/") > -1) ? "/" : "-";
    parts = date.split(sep);
    format = format.toLowerCase();
    if ( format === "us" ){
        m = parts[0];
        d = parts[1];
        y = parts[2];
    }
    else if ( format === "eu" ){
        d = parts[0];
        m = parts[1];
        y = parts[2];
    }
    else { // format assumed to be ISO
        y = parts[0];
        m = parts[1];
        d = parts[2];
    }
    return { year: y, month: m, day: d }
}

Then, it’s pretty trivial to get the parts of a date when submitting a form:

$("#form-id").submit(function(e){
    e.preventDefault(); // we don't want to do an actual form submit
    var $input, format, date;
    $input = $("#start");
    // determine the format by class name (us, eu, or iso)    
    format = APP.utils.getValueByClass($input, ["us","eu","iso"]);
    // get the day, month, and year values as properties to the "date" object
    date   = APP.utils.getDateValues($input.val(), format);
    // output will be: date: { year: 2014, month: 08, day: 28 }
    
    // post the data and display the response data
    $.ajax({
        type: "POST",
        url: "/rest/form/endpoint",
        data: {
            month: date.month,
            day:   date.day,
            year:  date.year
        }
    }).done(function(data){
        // server returns text "success" after saving the data
        $("#success").html(data);
    });
    return false;
});

That’s it. You get the day, month, and year values of a single-field date input, of which the format was determined by class name (in this case, we’re using the ‘us’ format), submit the data via AJAX, and display the returned data on the page. All with jQuery, because it makes using AJAX on the front-end pretty easy, and our app is already using it.

(here’s a jsFiddle to play with it – enter a date, change the format, then update to view the values below)
http://jsfiddle.net/g99ffked/2/

Set a value based on the presence of a class name (with jQuery)

In working on the web app that I’m employed to work on, I needed to check for the presence of a class name out of a few different possibilities so I could set the value of a variable based on that. If I only had to do this once or twice, and if there were only two options, I’d simply do:

var value = $('#element').hasClass('opt1') ? 'opt1' : 'opt2';

Piece of cake, right? Well, I had to do this many times, and once or twice there were more than two options. So I made a little function that will iterate over an array of possible values and check for a class name with that value, then return it.

Here’s what I came up with (yes, it uses jQuery because the web app uses it, so why not):

//"APP.utils" is the namespace where this function will live.
var APP = APP || {};
APP.utils = APP.utils || {};

APP.utils.getValueByClass = function( $container, values ){
    var val='';
    $.each(values, function( i, v ){
        if ($container.hasClass(v)){
            val = v;
            return false; // exit if we have a match
        }
    });
    return val ;
};

Here’s how you’d use it:

The HTML:

<!-- the 'us' class name indicates the date format, the placeholder directs the user -->
<input type="text" id="start" name="start" class="date us" placeholder="08/28/2014">
<p id="success"></p>

The JavaScript:

var format = APP.utils.getValueByClass( $('#start'), ['us','iso','eu'] );

The arguments are:

  1. a jQuery DOM object (not a string for the id or selector, a full jQuery object)
  2. an array of class names to look for (the first one found will be the returned value)

Now you’ve got a value for the format var to possibly use to determine how to split up the parts of a date input for form submission, which I will show in a later post.

Some code snippet updates…

So, I’ve been working with some of the code snippets I’ve posted on here previously, and I’ve got a few updates for them. These are pretty basic, and are written using jQuery, but could easily be done with native JavaScript – jQuery is already in use in the web app I work on, so there’s really no additional overhead (besides the extra 1/100th of a millisecond it takes to run the jQuery stuff, which I guess could be a problem if you’re running these functions millions of times, but if that’s the case, you probably have some other more important problems that need tackling).


This function will make sure an argument you’ve passed to it is a jQuery object, or convert it if it’s not. This is super handy for checking arguments before working with it inside your function:

https://error601.wordpress.com/2014/01/28/make-sure-youve-got-a-jquery-object/

Here’s the code – this now handles cases where you might just want to pass in the ID of a particular element, in which case the proper jQuery selector syntax will be added and a jQuery DOM object will be created and saved to a variable:

function jqObj(el){
    if (!el) { return false }
    var $el = el;
    if (!$el.jquery){
        $el = $(el);
        // if there's not a matching DOM element
        // then it's PROBABLY just an id string
        if (!$el.length){
            $el = $('#'+el);
        }
    }
    return $el;
}

It works like this:

(Note: don’t be a knucklehead – if you’re calling this function, give it an argument.)

a) If the argument is already a jQuery object, copy it to the new $el var, skip the other stuff, and return it.

– or –

b) Try to make it a jQuery DOM object (line 5) then check if there are, in fact, any elements in the DOM that match, but if not…

– then –

c) It’s likely that you’ve just passed a text string that matches an ID of a DOM element on your page. Maybe this is a bold assumption, but if you don’t want the hassle of passing a jQuery DOM object (or maybe this element doesn’t exist in the DOM yet, but you need the function to handle it when it does), then just tell this function the ID value and you’ll be good to go.

– so –

d) If all went well (and you weren’t a knucklehead and forgot to pass in at least SOME kind of argument), you should have a jQuery DOM object that matches an element on the page.


The next update is to this snippet:

https://error601.wordpress.com/2014/02/12/get-the-current-scripts-path/

In using it, I ran into an issue where it couldn’t get a the script file’s path if it was in the same directory as the HTML file where it was included.

Here’s the updated code:

function getScriptDir() {
    var src, path;
    src = $('script[src]').last().attr('src');
    if (src.indexOf('/') !== -1) {
        path = src.split('/');
        path.splice(path.length - 1, 1);
        return path.join('/') + '/';
    }
    else {
        return '';
    }
}

Now you can get the path to the current JavaScript file in case you would need it. I use it to dynamically grab a CSS file that contains styles needed for the current script to function properly and append it to the <head> element, if it’s not already in the DOM before the script is loaded. I’m sure there’s a much “cooler” way to do this using regex, but I guess I’m not that cool (and this works just fine).


I’ve got a few more snippets coming soon (I won’t wait another six months this time)… Stay tuned. 🙂

Switching to WordPress.com for blog posts (from Tumblr)

Yes, I could set up my own WordPress blog and write my own theme and deal with the hassle of keeping the back end updated… But why? I’ve got a busy life and the most important thing to me right now is sharing what I’m working on and any cool tidbits I come across in the process. This free WordPress.com blog, which is maintained by the fine WordPress folks themselves, lets me insert code with the “code” shortcode (that’s code for putting code in my WordPress post “code” shortcode).

$(function(){
    $('body').append('<blink>Look how cool I am.</blink>');
});

These WordPress posts will be blasted over Teh Intarwebz to Twitter and Tumblr – so, if anyone even cares, you can follow me just about anywhere (except Facebook – why would I share this stuff on Facebook? I’ll stick to sharing my personal Instagram pics there). 😛

“601? What the Devil?”

The link here is where the “601” concept comes from (the original Andromeda Strain movie), and it pretty much describes modern life with the onslaught of information and data spewing from everywhere on the planet!

So, if you’re into web development, follow me for random musings on jQuery/Javascript, HTML, CSS, etc. I’ll try to keep the posts relevant to that, but there may be some random posts ones thrown in to see if anyone’s paying attention!