Showing posts with label jquery. Show all posts
Showing posts with label jquery. Show all posts

Wednesday, 22 January 2014

Plain JavaScript

"I want to do this with pure JavaScript, not jQuery."
"How do I do this with raw JavaScript?"
"I don't like to use libraries, I like to use vanilla JavaScript."

Enough! Using a library like jQuery (or Closure or MooTools or...) is using plain/vanilla/raw/pure JavaScript. (I mean, unless you're using CoffeeScript, TypeScript, Dart, or other such languages.) Using a library doesn't change the language you're using.

jQuery is a library, not a language. You don't write things "in" jQuery. You write things with jQuery, in JavaScript. Or with MooTools, in CoffeeScript. Or with Closure, in Dart. You get the idea.

If you want to do things in the browser without a browser library, what you're doing is using the DOM API directly. Which is to say, writing with the DOM API, in (choose your language, probably JavaScript).

This isn't just a rant about prepositions. (It is a rant, but not just a rant.) It's about understanding what you're using, and how.

Tuesday, 24 September 2013

Submitting forms programmatically

Very short one today, folks, because something surprised me: If you use jQuery to submit a form programmatically, it will trigger submit handlers on the form. This surprised me because if you use the DOM to do it, it doesn't. Consider this HTML:

<form id="theForm" action="http://blog.niftysnippets.org" target="_blank" method="GET">
    <p>This one will trigger the submit handler:</p>
    <input type="submit" value="Click to send form normally">
</form>
<p>So will this one (which surprised me):</p>
<input type="button" id="jQuerySubmit" value="Click to use jQuery to send the form">
<p>This one won't (standard DOM behavior):</p>
<input type="button" id="domSubmit" value="Click to use HTMLFormElement#submit to send the form">

And this code (below the above in the HTML file):

$('#theForm').submit(function() {
  alert("jQuery submit handler called");
})[0].onsubmit = function() {
  alert("DOM submit handler called");
};

$("#jQuerySubmit").click(function() {
  $("#theForm").submit();
});

$("#domSubmit").click(function() {
  $("#theForm")[0].submit();
});

Clicking the jQuerySubmit button triggers the handlers, clicking the domSubmit button does not. Live copy

So if, like me, you're used to that handler not getting called when you submit programmatically, well, jQuery changes that! If you want the old behavior, use the DOM directly (it's only three more characters :-) ).

Happy coding!

Wednesday, 8 August 2012

jQuery - Element cleanup update

For those who saw my jQuery - Cleaning up when elements go away post yesterday, I've updated it showing how we can do this right now, today, without waiting for the enhancement (or if the enhancement is never accepted). Oh, and the enhancement went from six lines to three. Many thanks to Dave Methvin for showing how (in both cases). Enjoy!

Tuesday, 7 August 2012

jQuery - Cleaning up when elements go away

(Updated 08/08/2012.)

Have you ever wanted to get a notification when an element is removed from the DOM so you could clean up? For instance, maybe you have events hooked on a different object (like resize on window) that you want to unhook when the element goes away.

Recently I wanted to, and since I know that jQuery does cleanup when elements are removed (so it can clear out event handlers and its cache for data), I wondered if it triggers an event for us.

It doesn't, but we can still get what we want quite cleanly. I'll describe what I'm hoping we'll be able to do tomorrow, and what we can do today.

Tomorrow (I hope)

We can enhance jQuery to give us an event on cleanup — with just three lines of code and virtually no overhead. Inside jQuery's internal cleanData function, just after the line that currently reads if ( data && data.events ) {, we add this:

if ( data.events.jqdestroy ) {
  jQuery(elem).triggerHandler("jqdestroy");
}

Boom, that's it. Now if we need notification when an element is going away, we just hook up the event:

$("selector_for_the_element").on("jqdestroy", function() {
  // Do your cleanup
});

We use triggerHandler rather than trigger because we don't want bubbling (there's another way we can avoid bubbling, but it requires a couple more lines of code, and triggerHandler is more efficient anyway — thanks to Dave Methvin for that!).

Here's a copy of jQuery 1.8rc1 with the patch, you can play with it here — that latter link is a test page that generates 10,000 elements, 5,000 of which we hook the click event on (so that they have something in data.events), and two of which we hook the jqdestroy event on. Then we call html on the container element to destroy them all and time how long it takes. You can compare that with this version using an unmodified version of 1.8rc1. For me, the times are much of a muchness (on Firefox 14, both versions average ~142ms when the jqdestroy event isn't hooked, and when it is [on two elements] the version that fires it averages ~163ms).

What I like about this is that if nothing has hooked the event, the overhead is at most one extra property check (the if ( data.events.jqdestroy )) per element destroyed (zero overhead for elements that haven't had any events hooked at all), but it enables a completely familiar and straight-forward way to get notifications.

Well, okay, but is there really a need for it? It would seem so: jQuery UI duck-punches cleanData so they can clean up; TinyMCE goes further, monkey-patching several API calls (empty, replaceWith, and others) so it can clean up an editor attached to an element. And of course, I wanted it for my plug-in that needs to unhook window.resize if there are no more active copies of it.

Now, let me clear about something: To my mind, using this event is a last resort. It's a big hammer, and if you used it on a lot of elements, removing those from the DOM could lag a bit. Consider this example which hooks jqdestroy on 5,000 of the 10,000 elements. For me, the elapsed times go from ~163ms when firing it on just two elements to ~450ms firing on 5,000 (again Firefox 14). Now, 5,000 is a lot of elements to hook this event (or any other) on, and anything can be abused, the point is just...don't abuse it. :-) The best use cases for this will be things like TinyMCE's editors, or grid plug-ins that need to handle resize in code, that sort of thing — where there will be only a few elements with the event hooked.

I've opened an enhancement request on the jQuery Trac database for this, offering to do the patch and send a pull request if the idea has legs. If you think this is a good idea, your support would be welcome! I'm not saying we have to do it the specific way I've outlined in this post, I'm totally open to other ways to get there. Three lines of code, near-zero overhead, and a familiar paradigm seems pretty good to me, though.

Today

But what if that enhancement doesn't get adopted? Or if we need to do this right now, today, with the current version of jQuery? Do we have to hack the jQuery file, or resort to monkey-patching?

Nope. In the linked Trac ticket, Dave Methvin showed how we can do it today, by adding our own "special" event and watching for the teardown on it. This uses the event system, but we'll never actually receive the event in the normal way. Here's how it works:

First, we create a "special" event:

$.event.special.ourowndestroy = {
  teardown: function() {
    // Handle it here, `this` is the element being clean up
    console.log(this.id + " being destroyed");
  }
};

Then we force that to occur by hooking the event on the element, even though our handler will never get called:

$("selector_for_the_element").on("ourowndestroy", function() {
  // This is never called
});

Here it is in action using jQuery 1.7.2.

I've put a function there to make the point that the handler is never called (the action is in the teardown function); in reality I'd probably use $.noop or just false (shorthand for a handler that does return false) instead.

Now, when an element is being cleaned up, our teardown function will get called with this pointing at the element in question. Note that if we didn't hook the event on the element, we wouldn't force the teardown, so even though our handler isn't called, that's required.

Note: You'll also get the teardown call if you remove the event handler from the element (and then not when it's cleaned up, as it's not on there anymore), so if you're using this mechanism, either never remove the handler or handle the fact you get the call if you do.

So that's not an ideal way to do it, and it's not the way this stuff is normally done, but it's a passable workaround in the short term — and much better than monkey-patching jQuery's API on the fly.

Happy coding!

Wednesday, 8 June 2011

Release: place5 placeholder emulator plugin (alpha)

Just briefly, folks, I'm writing to announcing the alpha release of my place5 jQuery plugin. It's just a dead-simple plugin that emulates HTML5 placeholder attribute behavior on browsers that don't support it natively. On browsers that do, it stays out of the way (unless you tell it to take over), but it quietly does the job for you on browsers that don't do it themselves. Check it out! This is alpha, throw issues into the issue tracker on the project site if (when!) you find them.

Wednesday, 11 May 2011

jQuery 1.6.1 softens `attr` woes

jQuery 1.6's change to the attr function, and introduction of the prop function, raised quite a bit of confusion (and awareness) about properties and attributes. It was quite a breaking change, affecting a fair bit of code, and so jQuery 1.6.1 changes attr again to make it more backward-compatible. See the jQuery 1.6.1 blog post for details. According to the jQuery team, as of 1.6.1, most older use of attr should continue to work, although in many cases prop would be the preferred way to go.

Wednesday, 4 May 2011

Big compatibility break in jQuery 1.6

jQuery 1.6 is out, and unusually for jQuery, there's a pretty big API compatibility break with regard to the attr function. Details in the release notes, but basically, attr really is just for attributes now, whereas before it sort of conflated attributes and properties. There's a new prop function for interacting with properties.

Update 2011/05/11: jQuery 1.6.1 changes attr again to make it more backward-compatible for older code, details in the jQuery 1.6.1 blog post.

It's a seemingly-small change, but I've seen a lot of code that will be affected, especially around the "checked" property. If you've been checking whether a checkbox is checked using code along the lines of

// In an event handler, etc.:
if ($(this).attr('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.attr('checked')) {
// It's checked
}
...you'll need to update your code. Personally, I prefer going straight to the DOM element (that's what the jQuery team recommends as well):
// In an event handler, etc.:
if (this.checked)) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo[0] && foo[0].checked) {
// It's checked
}
...and the above has the advantage of working regardless of what version of jQuery you're using. But you could use prop instead if you like:
// In an event handler, etc.:
if ($(this).prop('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.prop('checked')) {
// It's checked
}
(There's also foo.is(':checked'), but that's awfully round-about.)

The big properties I see people stumbling over are "checked" (hence the above) and "value" (but hopefully you were already using val anyway).