Thanks for coming!
Fixing These jQuery is an HTML5 presentation designed to familiarise developers with basic approaches to debugging jQuery and JavaScript code. It also introduces many of the common pitfalls most people encounter at some point on their jQuery journey. It is always a work in progress!
Use the left ← and right → arrow keys or your mouse wheel to navigate.
You will have to debug it.
window.alert("Is Not A Debugger")
Why?
And if you aren't using them, you are insane.
jQuery.noConflict() run? (Wordpress does this by default.)Instead of this ugly, useless ugly alert...
$("ul li span").hover(function(e) {
var $t = $(this),
w = $t.width(),
o = $t.offset();
o.left = o.left + w;
alert(o);
span.text($t.text())
tip.offset(o).show();
});
debugger; statementDon't want to wait to set a breakpoint?
$("ul li span").hover(function(e) {
var $t = $(this),
w = $t.width(),
o = $t.offset();
o.left = o.left + w;
debugger;
span.text($t.text())
tip.offset(o).show();
});
The debugger; statement lets you set a breakpoint programmatically.
And just like a Christmas tree, you can find all sorts of awesome stuff underneath it.
When execution is paused in a debugger, you can control it - just like a VCR!
console.log & friends
$('#testElement')
.click({"foo":"bar"},function(e) {
console.info("The button has been pressed");
console.dir(e.data);
// You can pass an unlimited list of arguments
console.log(this,e);
$(this).append('Hello');
});
You don't have to modify your code at all to debug using the Console
window object (this === window)
log, debug, and dir
// Replace ".theElements" with your actual selector
console.log($(".theElements").length);
0, the elements are not in the DOMlength property of jQuery object is the easiest & fastest way to check
if a selector matched any elements. Don't use .size(), it's a waste of time.window or this, don't treat them like strings
$('this') // Wrong
$(this) // Right
> 0!By and large, jQuery works with elements that are in the DOM.
jQuery(document).ready(function($){});body.console.log($("yourSelector").length)$("yourSelector").length as a Watch Expression, wait for the breakpoint.console.log($("yourSelector").length) in your script, right before the code executes.
Don't just log .length!
You can log any type of variable.
If you log the entire jQuery object,
you can hover over the elements, if any, to see them highlighted in the page!*
* IE doesn't support any of this awesome stuff.
In fact, you can't interact with the page at all when you've paused execution in IE.
.html() or the result of inserting AJAX-fetched content.
var foo = $("div.foo").click(function() {
$(this).text("I was clicked!");
});
// foo.length == 1;
// nothing will happen when these divs are clicked.
$("<div>",{"class":"foo"}).appendTo(document.body);
$("div.bar").addClass("foo");
.bind() (and shortcuts) are only attached to elements that
are in the jQuery object when .bind() is executed..live() and .delegate
// All three examples will catch a click on any anchor that is ever in the page
$(document).bind("click",function(e) {
if ($(e.target).is("a") {
// this === document
}
});
$(document).delegate("a","click",function(e) {
//this === clicked anchor
});
$("a").live("click",function(e) {
//this === clicked anchor
});
bind() is not live()They do similar things. How are they different?
<div id="teddy" class="foo"> <div id="roosevelt" class="bar">
var log = function() { console.log(this.id); }
$(".foo").click(log);
$(".bar").live("click",log);
$(".foo").addClass("bar").removeClass("foo").trigger("click");
// logs 'teddy' twice
// 1) Because there is still a click handler attached to <div id='teddy'></div>
// even though it no longer has the class 'foo'
// 2) Because a "click" on an element with class "bar" bubbled to the document
.bind() creates a 1:1 association between element and handler.live() and .delegate() check to see if the target element of a bubbled event matches certain criteriaNew elements that match a selector do not automagically appear in existing collections
var li = $("#wheatBeers li");
// li.length == 3;
$("<li>",{text:"Weihenstephaner"})
.appendTo("#wheatBeers");
// guess what? li.length == 3
$("#foo") === $("#foo"); // false
$("#foo")[0] === $("#foo")[0] // true
var $myCoolWidget = $("#cycleMcParallax");
$myCoolWidget.cycleCount = 0;
add()
var red = $(".red"), green = $(".green");
// red.length == 5; green.length == 5;
green.add(red);
// green.length == 5;
green = green.add(red);
// green.length == 10;
this? I don't even...
$("#foo").click(function(e) {
$.getJSON("/ajax/page/",function(data) {
$(this).text(data.status); // IT'S NOT WORK
});
});
this example gives new jQuery users fits.
this is out of control!jQuery.fn.each, jQuery UIthis to be a DOM elementthis to be the options objectjQuery.eachthis to be the value being iterated, but weird.
$.each(["foo","bar"],function(i,value) {
// this == ["f","o","o"]
// value == "foo"
});
this here, use the second argument
this, might not. Be on the lookout.
If this isn't the this you think this is,
you can use the debugger and/or the console to confirm your suspicion.
this doesn't look like a DOM element from here!
this straightthis
$("#foo").click(function(e) {
var elem = this;
$.getJSON("/ajax/page/",function(data) {
$(elem).text(data.status); // ITS WORK NOW!
});
});
jQuery.proxy
var person = {
name:"Ace S. McCloud",
init:function() {
$("#sayHi").click($.proxy(function(e) {
// Now 'this' refers to the person object!
// We can still use e.target to reference the element
$(e.target).text(this.name);
},this));
}
};
this shall not pass!
// A simple function
function log(str) {
console.log(str);
};
// Logs an event object, because jQuery always passes the event object as the first argument to handlers
$("#foo").click(log);
You cannot override supplied function arguments by "passing arguments" to a named function: You'll end up executing the function!
// WRONG
$("#foo").click(log("I clicked it"));
// You just executed 'log', and failed to pass a function as a handler. Huh?
// It's the same as doing the following, and just as broken.
var l = log("I clicked it"); // l === undefined
$("#foo").click(l);
// RIGHT
$("#foo").click(function(e) {
log("I clicked it");
});
Was there an actual request?
Investigate the existence and results of AJAX calls
IE's debugging tools do not allow you to interrogate XHRs
You have to use a third-party HTTP Proxy tool like
Charles or Fiddler
beforeSend callback to check if the $.ajax() function is being reached.
$.ajax({
url:"foo.php",
data:{hello:"there"},
success:function(data) {},
beforeSend:function(xhr) {
console.debug("The XHR object has been prepared ",xhr);
}
});
XMLHttpRequest, and is not logged by these tools.script node and appends it to the head.iframe or Flash as a proxy to do the upload.successIf you are able to confirm that an XHR has indeed taken place, but your success callback isn't firing:
jQuery.getJSON or otherwise specifying a dataType
of JSON, jQuery will not fire the success callback if it cannot parse the response as valid JSON.jQuery.fn.load or otherwise appending HTML that you fetched via AJAX,
make sure the HTML is structurally valid and does not contain errors.this again!If your success callback is firing, but not working right
jQuery.ajax() has a context option
that sets the scope for all callbacks associated with that call.
var widget = {
elem:$("#scores"),
init:function() {
this.ajaxOptions = {
// The 'widget' object is 'this' when .init() runs,
// Preserve it as the context for all callbacks
context:this,
url:"scores.php"
};
return this;
},
getData(function() {
$.ajax($.extend({
success:function(data) {
// I can safely use 'this' to refer to the 'widget' object here
this.elem.html(data);
}
},this.ajaxOptions));
}
};
widget.init().getData();
You cannot reliably return the result of an AJAX callback back into the function that triggered it
// newContent === XMLHttpRequest
// NOT the raw server response
var newContent = $.get("remote.php",function(data) {
return data;
});
data is returned from the anonymous function, but not into newContent
var newContent;
$.get("remote.php",function(data) {
newContent = data;
});
$("#container").html(newContent); // Nothing is appended
newContent will contain the server response at some point in the future, but not immediately after $.get() runs."Two Wrongs" != "A Right"You cannot combine the previous two non-working techniques to create something that works
Don't feel bad, everybody tries this once
function getContent() {
var stuff;
$.get("remote.php",function(data) {
stuff = data;
});
return stuff;
}
var newContent = getContent(); // newContent === undefined
$.get() does not wait for the callback to finish,
neither will the containing getContent function
async:false?It's not like killing a kitten, but it is like locking one in a dark room and choosing not to feed it.
"It is much better to use callbacks and start thinking asynchronously, rather than procedurally. It's a bit of a struggle when you first start (since your code isn't necessarily run in line-by-line order), but it is very powerful and flexible once you get the hang of it."
nickf, 10 Nov 2009
$.get("http://www.google.com/search?q=javascript",function(data) {
$("#searchResults").html(data);
});
Yes, there are workarounds:
<script> tags in AJAX Content
Use the call stack to navigate the function invocation chain.
When you encounter some of your own code, debug everything you're passing into jQuery.
In this case, I'm passing a nonexistent method as a handler!
var get = {
ready: "for",
your: "funeral",
}; // BOOM
var class = "boom"; // BOOM
var foo = {class: "bar"}; // BOOM
var foo = {"class": "bar"}; // Actually OK. Quoted keynames ftw!
$("#pick-a-nick-basket")
.append("<div class='food'><h1>Roast Beef</a></div><a href='http://www.roastbeef.info'></h1>"); //sputter
$("form").focus(function(e) {
$(e.target).addClass("active").siblings(":input").removeClass("active");
});
// jQuery.fn.electricPiano is a fictional plugin that
// turns an input into a keyboard that plays synthesized sounds.
$("input.musical").electricPiano();
event.stopPropagation() or return false;for pete's sake!When you use a for loop, the iterator variable persists after the loop.
// OMG I heard jQuery.fn.each was like a HUGE perf hit!
// So I'm going to use a native for loop instead!
var lis = $("li"),
l = lis.length, // 5
li;
for (var i=0;i<l;i++) {
li = lis.eq(i);
li.click(function() {
alert("You clicked the "+ i +"th item");
// No matter which item is clicked, it always alerts 5!
});
}
http://jsfiddle.net/ajpiano/wq376/
The variable i is local to the function in which it's defined, not the loop
jQuery.each or jQuery.fn.each
// Make your own closure
var lis = $("li"), l = lis.length, i = 0, li;
for (i=0;i<l;i++) {
(function(i) {
// i is now local to this IIFE (immediately-invoked function expression)
li = lis.eq(i);
li.click(function() {
alert("You clicked the "+ i +"th item");
});
})(i);
}
// Just use jQuery's .each, it's not actually a serious performance concern
lis.each(function(i,elem) {
$(this).click(function(event) {
alert("jQuery's each says you clicked the "+ i +"th item");
});
});
http://jsfiddle.net/ajpiano/uNHYM/
Variable declarations and function declarations are hoisted to the top of the function in which they're declared.
var foo = true;
if (foo) {
function checkFoo() {
document.write("Foo is right and good, it is true");
}
} else {
function checkFoo() {
document.write("Foo is nefarious and evil, it is false");
}
}
checkFoo();
// checkFoo() always writes that foo is false,
// Even though you set it true "before" defining the function!
http://jsfiddle.net/ajpiano/ST5w5/
The code on the previous slide is actually interpreted like this!
var foo;
function checkFoo() {
document.write("Foo is right and good, it is true");
}
function checkFoo() {
document.write("Foo is nefarious and evil, it is false");
}
foo = true;
if (foo) { } else { }
checkFoo();
// Pretty crazy, amirite?
var statements, use commas to group variable declarationsthis