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

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/

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