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.).

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;

    };

})();

Trigger ‘onchange’ event when value changes. (jQuery)

You’d think that when you changed a value in a form input, the browser would fire the “onchange” event for that element. This is partially true. It will do so if a user changes the value, but not if it’s changed programmatically via JavaScript. So I needed some JavaScript (irony?), and here’s what I found:

http://stackoverflow.com/questions/3179385/val-doesnt-trigger-change-in-jquery

And here’s what I used (with a new function name, while leaving .val() alone):

$.fn.valChange = function(){
    var prev;
    if ( arguments.length > 0 ){
        prev = $.fn.val.apply(this, []);
    }
    var result = $.fn.val.apply(this, arguments);
    if ( arguments.length > 0 && prev != $.fn.val.apply(this, []) ){
        $(this).trigger("change");
    }
    return result;
};

And here’s how to implement it:

HTML:

<input type="text" id="test" value="Test">
<button id="btn1">Button 1</button>
<button id="btn2">Button 2</button>

JavaScript:

// send a log message to the console when it changes
$("#test").change(function(){
    console.log("The value has changed");
});
$("#btn1, #btn2").click(function(){
    // set the value when clicking the buttons
    // and if it's different than it was,
    // trigger the "onchange" event for the input
    $("#test").valChange($(this).text());
});

And here’s the obligatory JSFiddle to go with it:
http://jsfiddle.net/fbdskfz9/

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. 🙂

Get The Current Script’s Path

There were some tricky functions posted on the intarwebs for grabbing the URL of the current script’s directory (not the script itself). Most of them used some wacky RegEx trickery, but I figured there HAD to be a simpler way. So I made this utility function, which DOES use jQuery, but only because it’s SO DARN EASY to grab the DOM element I need to work with (and I’m already using jQuery in my project anyway).

Here it is:

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

What it does:

Gets the ‘src’ attribute for the last script on the page (DO NOT RUN THIS FUNCTION AFTER THE DOM LOADS – IT WON’T WORK), which should be the script where you call the function. Then it splits the components at the ‘/’ separators, knocks off the last element in the array (which is the script file itself), and joins it back together with slashes and adds another on the end for ease-of-use.

There you go. Feel free to use it wherever you want (I’m sure tons of people use this exact technique anyway, but whatever). Maybe you can even cut out the jQuery part for a fully-native JavaScript implementation (or maybe I will later).

Don’t Disorient Your Users With Links Used As Buttons

This is an oldie but goodie – a basic technique for preventing the page jumping to the top when clicking an <a> with an href="#" (or class="nolink" if you want to use a real link for accessibility purposes) when used for JavaScript functions (using JavaScript to maybe open a pop-up browser window – not considered ‘best practice’ in 2014, but still useful in some web apps, like if you need to see the whole web page while inputting data into a form in a pop-up window – or to perform a UI function with your <a> acting as a button).

The jQuery way (super easy – smashed onto one line for brevity):

$('body').on('click','a.nolink,a[href="#"]',function(e){e.preventDefault()});

The ‘modern’ browser way (Chrome, Firefox, Safari, IE9+) with native JavaScript, wrapped inside a super-cool IIFE:

(function(){
    var forEach = Array.prototype.forEach;
    var findAll = document.querySelectorAll.bind(document);
    forEach.call(findAll('a.nolink, a[href="#"]'), function(v){
        v.addEventListener('click', function(e){
            e.preventDefault();
        }, false);
    });
})();

(got a little help for that one from good ol’ Stack Overflow)

I know the jQuery way will also work for elements added after initial loading of the DOM, but not sure about the native code.

So there you go. That’s it.

Or… You know you could always use a <button> and style it to look like whatever you want (but I’ve run into problems in the past with browsers handling styles for <button> elements differently, that’s why I usually prefer <a> elements for ‘buttons’ that trigger JavaScript).

Make sure you’ve got a jQuery object

So, I’m writing a function that accepts jQuery DOM objects as arguments and sometimes even I forget that I’m supposed to pass a jQuery object when calling the function and pass only a selector. Here’s a short line of code that will make sure your function is working with a jQuery object:

$element = ($element.jquery) ? $element : $($element) ;

…or, if you’d rather check for falsiness and you don’t mind a single-line if statement maybe just…

if (!$element.jquery) $element = $($element) ;

Yes, it looks completely redundant and the else statement looks weird, but it’s totally cool, man. If you want to make sure your function doesn’t choke because some doofus (like me) forgot to wrap up the selector in $(), or maybe your element’s id has some weird characters in it and you need to pass the good ol’ document.getElementById('my_id') as your argument, it’s a tiny chunk of code for a whole bunch of insurance.

Here’s an example of this in action in a function that shows an alert when clicking a button element:

JavaScript:

function annoyingAlert($btn, msg){
    $btn = ($btn.jquery) ? $btn : $($btn) ;
    $btn.click(function(){
        alert(msg)
    }
}

HTML (and inline JavaScript):

<button id="this_button">Click Me</button>
<script type="text/javascript">
    annoyingAlert('#this_button', 'Good job genius, you clicked the button.');
</script>