Best jquery questions in November 2011

jQuery code is too verbose, would like pointers on how to make it shorter

40 votes

I have some jQuery code that I would like reviews and pointers on on how to bring its line count down and shorten.

$('#p1').click(function() {
    $('#list').fadeOut(450);
    $('#q1').delay(600).fadeIn(450)
});
$('#p2').click(function() {
    $('#list').fadeOut(450);
    $('#q2').delay(600).fadeIn(450)
});
$('#p3').click(function() {
    $('#list').fadeOut(450);
    $('#q3').delay(600).fadeIn(450)
});
$('#p4').click(function() {
    $('#list').fadeOut(450);
    $('#q4').delay(600).fadeIn(450)
});

...

$('#p12').click(function() {
    $('#list').fadeOut(450);
    $('#q12').delay(600).fadeIn(450)
});
$('#p13').click(function() {
    $('#list').fadeOut(450);
    $('#q13').delay(600).fadeIn(450)
});

Can this code be better optimised? Or at least made less verbose?

You can use a for loop, but you should make sure the loop counter's value gets into a correct scope for click event handler:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};
for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

Otherwise the delay and fadeIn would get applied to #q13 element exclusively, since actual counter (with its final value of 13) would get into closure.


EDIT: Since quite a lot of answers got it wrong here, I'll attempt to explain more precisely what's going on in this code, as it seems to be pretty confusing.

The "natural" solution with injecting the click handler directly into loop would be the following:

for(var i = 1; i < 14; i++) {
    $('#p'+i).click(function() {
        $('#list').fadeOut(450);
        $('#q'+i).delay(600).fadeIn(450)
    });
}

But this is not at all equivalent to the extended form, which lists all the 13 variants one after another. The problem is that while there are indeed 13 functions created here, they are all closed over the same variable i, whose value changes. It finally arrives at the value of 13 and the loop ends.

Some time later the functions attached to #p1...#p13 elements are called (when one of those elements are clicked) and they use that final value of i. This results in only #q13 being animated.

What needs to be done here is to do something called lambda lifting and eliminate the free variable i, whose value gets inadvertly changed. A common technique for that is to provide a "factory function" which accepts value for our variable and outputs an actual function which we'll use as event handler:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};

Since the scope of k parameter is local to clickHandler, every call to clickHandler gets different k variable. The function returned from clickHandler is therefore closed over different variables, which can in turn have different values. This is exactly what we need. We can then call clickHandler from our loop, passing it the counter's value:

for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

I hope this makes the difference somewhat clearer.


EDIT: As Esailija pointed out in the comments, it is also possible to use jQuery.each to achieve similar effect:

$.each(new Array(13), function(idx) {
    $('#p' + (idx + 1)).click(function() {
        $('#list').fadeOut(450);
        $('#q' + idx).delay(600).fadeIn(450);
    });
});

This is probably the solution of choice if you're already aware of the closure/scoping issue I've tried to outline above.

My JavaScript patterns/practices stink. Where should I seek help?

28 votes

I've been working almost exclusively on back-end tasks for the past few years, and I've just noticed that most JavaScript (and CoffeeScript) projects have got a helluva lot prettier in my absence.

I work primarily in a rails environment, and almost all my JavaScript/jQuery used to look like this:

$(an_element).an_event(function() {
  stuff_i_want_to_do;
})

$(another_element).some_other_event(function() {
  some_other_stuff_i_want_to_do;
})

Callbacks aside, that's pretty much been it.

Anyhow, was just browsing through some other folks' code and noticed many javascripters have been getting a lot prettier in my absence. This isn't complex, but it's typical of the newer/better approach to JavaScript I've been seeing:

jQuery -> 
  if $('#products').length
    new ProductsPager()

class ProductsPager
  constructor: (@page = 1) ->
    $(window).scroll(@check)

  check: =>
    if @nearBottom()
      @page++
      $(window).unbind('scroll', @check)
      $.getJSON($('#products').data('json-url'), page: @page, @render)
#

  nearBottom: =>
    $(window).scrollTop() > $(document).height() - $(window).height() - 50

  render: (products) =>
    for product in products
      $('#products').append Mustache.to_html($('#product_template').html(), product)
    $(window).scroll(@check) if products.length > 0

I've been looking for resources on modern best practices/patterns for JavaScript (and/or CoffeeScript), but I haven't had much luck. So in short, where should I look to be brought up to speed re: best javascript/coffeescript modern patterns & practices?

JavaScript resources

Patterns For Large-Scale JavaScript Application Architecture

Essential JavaScript Design Patterns For Beginners, Volume 1.

JavaScript Patterns

jQuery specific

Tools For jQuery Application Architecture

CoffeeScript

http://coffeescriptcookbook.com/chapters/design_patterns/

Workaround for lack of CSS feature to "suppress inherited styles" (and backgrounds?)

21 votes

I have a DOM situation that looks like this:

  • A is an ancestor of B, which is in turn an ancestor of C

  • Initially, A has styles that inherit to B and C

  • I wish to temporarily highlight B by giving it a highlighted class

    ...however...

  • I want to "escape" the highlighting on C so it changes as little as possible

It seems this is not possible within the cascading paradigm of CSS. The only way to "un-apply" a style is to apply an overriding style. My problem is that the highlight code is in a plugin that wants to play well with an arbitrary page's existing CSS...my selectors are like this:

/* http://www.maxdesign.com.au/articles/multiple-classes/ */
.highlighted.class1 {
    background-color: ...
    background-image: ...
    ...
}
.highlighted.class2 {
   ...
}
/* ... */
.highlighted.classN {
   ...
}

Background is a tricky one...because although it is not an inherited CSS property, changing an ancestor's background can alter a descendant's appearance (if they have transparent backgrounds). So it's an example of something that causes the kind of disruption I'm trying to negate. :-/

Are there any instances of how people have suppressed the inheritance of changes in this fashion? Perhaps using tricks such as caching computed styles? I'm looking for any default technique that can be at least a little smarter than a function-level hook that says "hey, the plugin highlighted this node...do what you need to visually compensate."


UPDATE I have created a basic JsFiddle of this scenario to help make the discussion clearer:

http://jsfiddle.net/HostileFork/7ku3g/

Manage all CSS changes in javascript so that they're easily reversible. As you said, it's not possible in pure CSS, but you can cache the styles via javascript.

In steps:

  1. define what kind of elements will be ignored inside the highlight
  2. check if you have a background-image in any of the parents
  3. if you do, save the image and that element's offset()
  4. grab the descendant elements
  5. save their current CSS styles (before the highlight)
  6. apply the highlight styles to the target
  7. re-apply the saved styles for the descendants
  8. if there was a parent with a background image, calculate and offset the image accordingly

This keeps all styles for the inner elements intact, and accounts for background images that "fall through" transparent backgrounds.

Here's a test: http://jsfiddle.net/W4Yfm/2/

This leaves a messy sea of style attributes behind though, you might want to clean it up. There are probably unnacounted edge cases too.

To be most reliable you should use a technique like @Jan pointed out (draw the highlight on canvas), but you won't have support in IE < 9, and it's probably too slow for mobile.

When would I use JQuery.Callbacks?

13 votes

I was looking through new stuff added to jQuery 1.7 and I saw they now have jQuery.Callbacks() http://api.jquery.com/jQuery.Callbacks/.

The documentation shows you how to use jQuery.callbacks() but not any applicable examples of when I would want to use them.

It seems you can add/remove callbacks from a callbacks list and you can do jQuery.callbacks().fire(args), but this just fires off ALL of the callbacks in that list. Maybe I am missing something but this doesn't seem very useful.

In my head when I first saw this new functionality I thought you would be able to use it with key/value pairs. Which would then provide a simple way to manage callback functions in a single place in your application. Something like

$.callbacks.add("foo", myFunction);

and then for example if I wanted to call that callback at the end of my function I could do something like

$.callbacks().fire("foo", args);

However it doesn't look like you can fire off specific callbacks, you can only fire off all of them with the given arguments or none of them.

The closest thing I saw was being given the ability to give the .fire() function a context to set the "this" property

.fireWith(context, args)

but this doesn't really help much either.

  1. Am I misunderstanding the documentation?

  2. If this is the desired functionality what are some applicable examples where this is useful.

To expand on @Rockets answer a bit and clear up some confusion:

The reason that one might need to use jQuery's $.Callbacks is multifaceted:

  1. The user has alot of code in one function and wants to split it up
  2. They take that information and send it through the jQuery callback function which then allows them to have split their code into better manageable pieces with which to work with.
    So (for example) if you look at @Rocket's code:

    var clickCallbacks = $.Callbacks();
    
    clickCallbacks.add(function() { //one one function piece
        //parse and do something on the scope of `this`
        var c = parseInt(this.text(), 10);
        this.text(c + 1);
    });
    clickCallbacks.add(function(id) { //add a second non-related function piece
        //do something with the arguments that were passed
        $('span', '#last').text(id);
    });
    
    $('.click').click(function() {
        var $ele = $(this).next('div').find('[id^="clickCount"]');
        clickCallbacks.fireWith($ele, [this.id]); //do two separate but related things.
    });
    
  3. What you can now have is multiple callback batches of function which you can now call whenever you deem it be necessary without the need to make so many changes throughout out your code.

jQuery 1.7 - Turning live() into on()

11 votes

My application has dynamically added Dropdowns. The user can add as many as they need to.

I was traditionally using jQuery's live() method to detect when one of these Dropdowns was change()ed:

$('select[name^="income_type_"]').live('change', function() {
    alert($(this).val());
});

As of jQuery 1.7, I've updated this to:

$('select[name^="income_type_"]').on('change', function() {
    alert($(this).val());
});

Looking at the Docs, that should be perfectly valid (right?) - but the event handler never fires. Of course, I've confirmed jQuery 1.7 is loaded and running, etc. There are no errors in the error log.

What am I doing wrong? Thanks!

The on documentation states (in bold ;)):

Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().

Equivalent to .live() would be something like

$(document.body).on('change', 'select[name^="income_type_"]', function() {
    alert($(this).val());
});

Although it is better if you bind the event handler as close as possible to the elements, that is, to an element being closer in the hierarchy.

Update: While answering another question, I found out that this is also mentioned in the .live documentation:

Rewriting the .live() method in terms of its successors is straightforward; these are templates for equivalent calls for all three event attachment methods:

$(selector).live(events, data, handler);                // jQuery 1.3+
$(document).delegate(selector, events, data, handler);  // jQuery 1.4.3+
$(document).on(events, selector, data, handler);        // jQuery 1.7+

how to check if there is already a click/event asociated to an element

11 votes

lets say i have

function trigger(){

    $('a.pep').each(function(){
            $('a.pep').click(function(){
            console.log($(this).val());
        });
    });
}
function push(){
    $('body').append('<a class="pep">hey mate i have no trigger yet</a>');
    trigger();  //now i do but the others have duplicated trigger
}
$(document).ready(function(){
     $('a.push').click(function(){
        push();
     });
});

So it seems that the click event is being applied twice/+ because the console.log is lauched more than once by click

How can i prevent this?

The problem is that you call $('a.pep').click() lots of times. (In fact, you bind as many click handlers as there are matching elements to each element. And then you do it again every time one of them is clicked.)

You should lever the DOM event bubbling model to handle this. jQuery helps you with the on method:

$(document.body).on('click', 'a.pep', function() {
    console.log('element clicked');
    $(document.body).append('<a class="pep">Click handlers handled automatically</a>');
});

See a working jsFiddle.

Note that I have removed the val call, because a elements can't have a value... Note also that the on method is introduced in jQuery 1.7; before that, use delegate:

$(document.body).delegate('a.pep', 'click', function() {

jQuery 1.7 is *still* returning the event.layerX and event.layerY error in Chrome

9 votes

Update: I worked it out - it was a Chrome Extension loading its own version of jQuery (whoops!)...


What am I doing wrong? Am I misunderstanding the problem or is it something else entirely?

On my page I was using jQuery 1.6.4 from the Google CDN. This would, of course, generate the error:

event.layerX and event.layerY are broken and deprecated in WebKit. They will be removed from the engine in the near future.

I read here that jQuery 1.7 removed this issue. However, after updating my application to 1.7, I'm still seeing it. I'm using the Microsoft CDN until Google put the link up.

Things I've tried before posting this:

  • Clearing the browser cache
  • Changing back to jQuery 1.6.4 (still happens - obviously)
  • Using jQuery 1.7-specific code to make sure 1.7 is actually being loaded - .on() works fine when I use 1.7 but obviously gives undefined errors with 1.6.4 - I thought this should prove 1.7 is actually running
  • Commenting out and removing all other Javascript from my application - everything except for jQuery 1.7. Still triggers the error.

Any ideas?

Worked it out - it wasn't immediately obvious either, but posting this here for anyone who gets tripped up by this in future:

It was a Chrome Extension that used an old version of jQuery causing the issue. Disabling all Extensions and re-enabling one at a time helped me find the perpetrator. Hope this helps for anyone else with this issue in the future!

What's the difference between `on` and `live` or `bind`?

9 votes

In jQuery v1.7 a new method, on was added. From the documentation:

‘The .on() method attaches event handlers to the currently selected set of elements in the jQuery object. As of jQuery 1.7, the .on() method provides all functionality required for attaching event handlers.’

What's the difference with live and bind?

on() is an attempt to merge most of jQuery's event binding functions into one. This has the added bonus of tidying up the inefficiencies with live vs delegate. In future versions of jQuery, these methods will be removed and only on and one will be left.

Examples:

// Using live()
$(".mySelector").live("click", fn);

// Equivalent `on` (there isn't an exact equivalent, but with good reason)
$(document).on("click", ".mySelector", fn);
// Using bind()
$(".mySelector").bind("click", fn);

// Equivalent `on`
$(".mySelector").on("click", fn);
// Using delegate()
$(document.body).delegate("click", ".mySelector", fn);

// Equivalent `on`
$(document.body).on("click", ".mySelector", fn);

Internally, jQuery maps all these methods and shorthand event handler setters to the on() method, further indicating that you should ignore these methods from now on and just use on:

bind: function( types, data, fn ) {
    return this.on( types, null, data, fn );
},
live: function( types, data, fn ) {
    jQuery( this.context ).on( types, this.selector, data, fn );
    return this;
},
delegate: function( selector, types, data, fn ) {
    return this.on( types, selector, data, fn );
},

See https://github.com/jquery/jquery/blob/1.7/src/event.js#L965.

jQuery Validation custom validation adding no space validation

9 votes

I have a form where the user can update his name and last name. I use jQuery validation to validate the form. How can I validate if the user put spaces?

here's what i have:

<script>
$(document).ready(function(){   
  $('#submit').click(function() {
  var valid = $("#myform").valid();
  if(!valid) {
    return false;
  }
  $.ajax({
    type: "POST",
    url: 'save',
    data:  $('#myform').serialize(),
    dataType: 'json',
    cache: false,
    success: function(result) {
      // redirect to another page
    }
  });
  });
  });
</script>

</head>
<body>


<form id="myform" method="post" action="">
<fieldset>
<legend>update name</legend>
<p>
 <label for="fname">Name</label>
 <em>*</em><input id="fname" name="fname" size="25" class="required" minlength="2" />
</p>
<p>
 <label for="lname">Last Name</label>
 <em>*</em><input id="lname" name="lname" size="25" class="required" minlength="2" />
</p>
<p>
 <input id="submit" type="submit" value="Submit"/>
</p>
</fieldset>
</form>

thanks

use this for your javascript code:

$(document).ready(function() {

  jQuery.validator.addMethod("noSpace", function(value, element) { 
     return value.indexOf(" ") < 0 && value != ""; 
  }, "Space are not allowed");

  $("#myform").validate({
    errorLabelContainer: $("#error"),
    rules: {
      fname: { required: true, noSpace: true },
      lname: { required: true, noSpace: true }
    },
    messages: {
      fname: { required: 'Please enter your name' },
      lname : { required: "Please enter your last name" }
    }
  });

  $('#submit').click(function() {
    var valid = $("#myform").valid();
    if(!valid) {
      return false;
    }
    $.ajax({
      beforeSend: function() {
        // display loading message
      },
      type: "POST",
      url: 'save',
      data:  $('#formdata').serialize(),
      dataType: 'json',
      cache: false,
      success: function(result) {
        if(result.error) {
          // show error message
        }
        else {
          // redirect to another page
        }
      },
      error: function (response, desc, exception) {
        // show ajax error
      },
      complete: function() {
        // hide loading message
      }
    });
  });
});

Adding cookies to drag and drop

9 votes

I'm creating an drag and drop plugin and I thought to make it a little unique i would add a cookies feature to save the position of the dragged elements.

I'm currently using the following code for the get and set cookies:

$.setCookie = function(c_name, value, exdays) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString());
    document.cookie = c_name + "=" + c_value;
}
$.getCookie = function(c_name) {
    var i, x, y, ARRcookies = document.cookie.split(";");
    for (i = 0; i < ARRcookies.length; i++) {
        x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
        y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
        x = x.replace(/^\s+|\s+$/g, "");
        if (x == c_name) {
            return unescape(y);
        }
    }
}

These work fine. But What I can't get to work is this:

if (o.cookies === true) {
    $(oj).mouseup(function() {
        var currentPos = $(oj).position();
        $.setCookie('tposition22' + $(oj).index(), currentPos.top, 365);
        $.setCookie('lposition22' + $(oj).index(), currentPos.left, 365);
        alert('Cookies Set!')
    });
    $(function() {
         var savedLeftPosition = $.getCookie('lposition22' + $(oj).index());
         var savedTopPosition = $.getCookie('tposition22' + $(oj).index());
         $(oj).css({
             top: savedTopPosition,
             left: savedLeftPosition
         });
    });
}

Code Description: o.cookies === true is to check if cookies is set to true; setCookie works(I checked); oj is referring to this, the selector.

Problem: I need to be able to get the value of the cookie. Because, im currently trying to make the value the position of the dragged element and then retrieving it.

As you can see in $.setCookie('tposition22' + $(oj).index(), currentPos.top, 365);, currentPos.top is in the value spot. To get the Y position of the dragged element.

Main Question: Is there a way to retrieve the value of a cookie?

Sure you can retrieve the value of a cookie. I like not reinventing the wheel, though, and since you're already in jQuery-ville, why not use a jQuery cookie plugin? I think there's even an "official" one. Should provide simple access to everything you need for interacting with cookies (really, just getting and setting one!).

With regard to your specific code, where is o (from o.cookies) coming from, and why is it expected to be a boolean?

Side note: almost all code can benefit from properly-named variables. Letting your minifier reduce down to single-letter will keep your code more readable.

Ambiguity in interpreting optional parameters

8 votes

The signature of the jQuery function .on is

$(elements).on(events [, selector] [, data], handler);

where selector and data are optional. Therefore, the function call

$(elements).on(var1, var2, var3);

could be interpreted with var2 as either selector or as data. Is there ambiguity?

More generally, how is ambiguity from optional parameters dealt with for any other jQuery function?

If only one of the selector and data parameters is provided and the value is a string it is assumed to be a selector.

From the jQuery doco for .on():

The data argument can be any type, but if a string is used the selector must either be provided or explicitly passed as null so that the data is not mistaken for a selector. Best practice is to use an object (map) so that multiple values can be passed as properties.

A similar principle applies to other jQuery methods with optional parameters.

jQuery's noConflict: Just a quick thought

8 votes

The documentation says you can use $.noConflict() like this:

jQuery.noConflict();
(function($) { 
  $(function() {
    // more code using $ as alias to jQuery
  });
})(jQuery);
// other code using $ as an alias to the other library

It also states that calling it returns an instance of the jQuery object, so I could do this:

jQuery.noConflict()(function(){
    // code using jQuery
}); 
// other code using $ as an alias to the other library

However, is this combination valid?

(function($) { 
  $(function() {
    // more code using $ as alias to jQuery
  });
})(jQuery.noConflict());
// other code using $ as an alias to the other library

If so, is there a reason to not do it this way? And also (if it works), why not always use this method to guarantee that inside of our closure, $ == jQuery?

The last method works as well - jQuery.noConflict() returns the jQuery object, which is passed as the $ argument to the function.

I don't see a reason to not do it that way and would prefer it to the other methods.

CSS/HTML : how to make something become absolute positioned once you scroll by it

8 votes

I'm new to CSS and HTML here and I'm trying to learn how to make something become absolutely positioned once you scroll by it on the page.

Here's an example of what I mean: http://fab.com/help/ (you don't need an account to scroll). When you scroll down, the black menu bar at the top disappears and the white menu bar with "How can we help you" becomes absolutely positioned.

I created a an example with a similar menu system,

http://jsfiddle.net/jkdbP/

but I don't know where to start to make it become absolutely positioned once it's scrolled by, thanks a lot for any insights!

See this jsFiddle: http://jsfiddle.net/jkdbP/2/

var menuTop = $('.menu').offset().top;
var menuClone = $('.menu').clone().addClass('fixed');

$(window).bind('scroll', function() {
    var scrollY = window.pageYOffset;

    if(scrollY > menuTop) {
        if(menuClone.parent().length === 0) {
            menuClone.appendTo($('.menu').parent());
        }
    } else if(menuClone.parent().length > 0) {
        menuClone.remove();
    }
});

You can use jQuery's .offset().top to get the Y-position of your menu, and window.pageYOffset (or document.body.scrollTop for old IE compatibility) to get the page's scroll offset. You can then handle the window's scroll event.

jQuery ajax this.id undefined

7 votes

I have a list of items I delete using AJAX.

This list is a simple list with divs and each div as an id so when the item is removed from the database I return true and then it removes the line.

Here my code:

HTML

<div id="row1">
<div>item1</div>
<div><a href="...">view</a></div>
<div><a id="1">delete</a></div>
</div>

JS

$('.delete').click(function () {
    if (!confirm('Are you sure you want to delete?')) {
        return false;
    }
    $.ajax({
        type: "POST",
        url: '/delete_record',
        data: 'id=' + this.id,
        cache: false,
        success: function (result) {
            if (result == 'good') {
                $('#row' + this.id).remove();
            }
        }
    });
});

For some reason the this.id does not work because this.id is undefined ... why? I have id="1" on my a href.

The this you are using refers to the ajax object because there is a new scope within that function. If you want to access variables outwide your scope, you have to declare them before the ajax call.

$('.delete').click(function () {
    if (!confirm('Are you sure you want to delete?')) {
        return false;
    }
    var _this = this;
    $.ajax({
        type: "POST",
        url: '/delete_record',
        data: 'id=' + this.id,
        cache: false,
        success: function (result) {
            if (result == 'good') {
                $('#row' + _this.id).remove();
            }
        }
    });
});

symfony 2 equivalent for url_for() function in symfony 1

6 votes

In Symfony 1 we can access an action in template page as for example url_for('modulename/actionname') without writing anything in routing.yml.

how is this possible in Symfony2?,that is if i have to access more than one action in a twig without specifying in routing.this is useful while using ajax.

Thanks in advance

If I understand your question correctly, you're asking how you can generate a url by passing a module name and action name, instead of a route name. Is that right?

I don't think this is possible in Symfony2. If you take a look at the generate method in Symfony\Component\Routing\Generator\UrlGenerator you'll see that it expects a route's name as the first parameter. Also, Symfony2 doesn't natively support the generic routes that symfony 1 does (shown below for reference).

default_index:
  url:   /:module
  param: { action: index }

default:
  url:   /:module/:action/*

Without these generic routes, you can't simply access /myModule/myAction without actually defining a route for it. And don't forget that Symfony2 now uses bundles, which would complicate this further.

So for whatever actions you want to access, you'll need to write routes for them.

In order to actually generate the URLs...
- From a controller: $this->generateUrl($routeName);
- From a PHP template: $view['router']->generate($routeName);
- From a Twig template: {{ path('_routeName') }} or {{ url('_routeName') }} for an absolute URL

jQuery 1.7 selector error

6 votes

this is my very first question.

I'm working on a fully Ajax system with jQuery, and it works fine with 1.6.2. When I tried to upgrade it to 1.7, this piece of code stopped working properly:

$("a[class!='']").live("click",function(e){
    e.preventDefault();
});

In 1.6.2, it prevents all hyperlink tags from working as a link if they have a class, but in 1.7 it stopped ALL links from working as real links, even those without classes.

Fiddle: http://jsfiddle.net/hBehg/

Use $('a[class]'), this will select all elements which have the class attribute. As I said in my comment, checking for an empty value might not work if the element does not even have a class attribute.

Update: As pointed out by @Sidnicious, the documentation describes that this selector will also select those elements which do not have that attribute. If it didn't in 1.6, then it actually must have been a bug in that version, or they changed the description without mentioning it.

Of course, if you indeed have an empty class attribute, i.e. <a class="">, this will not work.

DEMO

Update 2: As @lonesomeday mentions in his comment, $('a[class][class!=""]') does work as you intended with $(a[class!=""]).

As others said, you can change to on in jQuery 1.7, which unifies the event handling methods, but it won't solve your particular problem.

Firefox + jQuery on OS X doesn't show errors thrown in AJAX handlers?

6 votes

When an error occurs in a jQuery AJAX handler on Firefox + jQuery + OS X the error seems to be silently ignored.

Example code: http://jsfiddle.net/bGuX9/

Chrome correctly reports both errors in the JavaScript console:

error in Chrome

But Firebug in Firefox only reports one error, even though two have been thrown (as evidenced by the “Throwing error: in ajax”):

error in Firefox

The above is Firefox 8 on OS X 10.6, but I've noticed the issue as far back as Firefox 3.5. Firefox on Windows (tested with 8) doesn't seem to be affected.

What's up with this? Is this a known issue?

A couple notes:

  • I don't believe this is an issue with Firebug, as the error doesn't appear in Firefox's error console either.
  • I know that I can use a try/catch block in my event handler and catch the error there, but that doesn't help me debug arbitrary code.

After much searching, it looks like this issue is caused by the Adblock Plus extension, as it only occurs when the extension is enabled.

A bug has been filed on the Adblock Plus forum: https://adblockplus.org/forum/viewtopic.php?f=11&t=8761

jQuery doesn't work with <col> XML tags

5 votes

I was using jQuery doing some XML work. Then jQuery told me it can't find the <col> tag giving me empty data. After having a talk with jQuery, seems like it just doesn't like to work with <col> XML tags, maybe some expert can explain this to me?

Here's my XML:

<field>
<property>
    <label>Matrix Question</label>
    <rows>
        <row>row - 1a</row>
        <row>row - 2a</row>
        <row>row - 3a</row>
        <row>row - 4a</row>
    </rows>
    <cols>
        <col>col - 1</col>
        <col>col - 2</col>
        <col>col - 3</col>
        <col>col - 4</col>
        <col>col - 5</col>
    </cols>
    <isrequired>true</isrequired>
</property>

Here's my code for it:

var xmlWithCol = "<field> <property> <label>Matrix Question</label> <rows> <row>row - 1a</row> <row>row - 2a</row> <row>row - 3a</row> <row>row - 4a</row> </rows> <cols> <col>col - 1</col> <col>col - 2</col> <col>col - 3</col> <col>col - 4</col> <col>col - 5</col> </cols> <isrequired>true</isrequired> </property> </field>";
var xmlWithoutCol = "<field> <property> <label>Matrix Question</label> <rows> <row>row - 1a</row> <row>row - 2a</row> <row>row - 3a</row> <row>row - 4a</row> </rows> <cols> <colm>col - 1</colm> <colm>col - 2</colm> <colm>col - 3</colm> <colm>col - 4</colm> <colm>col - 5</colm> </cols> <isrequired>true</isrequired> </property> </field>"

$(xmlWithCol).find("cols").each(function ()
{
    alert($(this).html());
});

$(xmlWithoutCol).find("cols").each(function ()
{
    alert($(this).html());
});

as you can see my first output is

col - 1 col - 2 col - 3 col - 4 col - 5 

then I found out jQuery doesn't like <col> tag, I changed it to < colm >< /colm > instead, and it gives me this:

<colm>col - 1</colm> <colm>col - 2</colm> <colm>col - 3</colm> <colm>col - 4</colm> <colm>col - 5</colm> 

which is what I want.

How can I make my jQuery love the <col> tag?

I don't claim to be an expert, but <col> exists in HTML as an empty element. You can't give that element text, even if you try to use it as an XML element, because jQuery would be breaking the HTML DOM rules otherwise.

Since jQuery was made to work with HTML rather than XML, I can't think of a good workaround besides simply not using the name <col>...

My function won't run?

5 votes

I'm getting tired of firebug telling my vars aren't defined...

I have a button: next. When the button is clicked, I want it to load a php page into a div, assigning the php page the variable representing the next page.

To do this, I have a variable crntpage that stores the value of the current page. In order to calculate what the var for the next page must be I have a function called next which calculates the value and returns it.

Let's assume that we are on page 5:

javascript

$(document).ready(function() {

$.ajax({
    url: 'pagination.php',
    type: 'POST',
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    async: true,
    cache: false,
    success: function (pages) {
                last = pages['last'];
                crntpage = 1;

                function nxt(b) {
                    if (b == last) {
                        next  = last;
                    } else {
                        next = b + 1;
                    }
                    return next;
                }

                $('#next').live('click', function() {
                    crntpage(next);
                    $('#content').load('getposts.php?pagenum=' + nxt(crntpage));
                    return false;
                });
    }
});
});

html

<div id="previous"> 
        <a href=''> <-Previous</a>
</div>

I keep getting an error saying that next isn't defined. My guess is because my nxt function is not receiving the value of last. What am I doing wrong?

What you are trying to do with the nxt function can be accomplished more idiomatically with Math.min():

$('#next').live('click', function() {
    crntpage = Math.min(crntpage + 1, last);
    $('#content').load('getposts.php?pagenum=' + crntpage);
    return false;
});

You should also prefix variable declarations with the var keyword, so as not to pollute the global namespace.

Here's the revised code together:

$(function() {

    var currentPage = 1;
    var last = 1;

    $('#next').live('click', function() {
        currentPage = Math.min(currentPage + 1, last);
        $('#content').load('getposts.php?pagenum=' + currentPage);
        return false;
    });

    $.ajax({
        url: 'pagination.php',
        type: 'POST',
        contentType: "application/json; charset=utf-8",
        dataType: "json",    
        cache: false,
        success: function (pages) {
            last = pages['last'];
        }
    });

});

How to call function on parent page from iframe using jQuery?

4 votes

I have an upload form that posts to a hidden iframe. I am attempting to call a function on the parent page from the iframe, but am getting the error "top.stopUpload is not a function".

What is the correct way to do this?

PARENT PAGE:

$(document).ready(function() {

    $('#document_upload').submit( function() {

        $('#upload_progress').show();

     });

    function stopUpload(success){

        if (success == 1){
            $('#result', window.parent.document).html(
            '<span class="msg">The file was uploaded successfully!<\/span>');
        }

        else {
            $('#result', window.parent.document).html(
            '<span class="emsg">There was an error during file upload!<\/span>');
        }

        $('#upload_progress').hide();

        return true;

    }

})

IFRAME:

$(document).ready(function() {

   top.stopUpload(<?php echo $result; ?>);

}

Your stopUpload() function is scoped to the anonymous function you pass to $(document).ready().

You will need to ensure its scope is visible to the iframe. One way to do that would be to assign the function to window, the global object in a browser.

Another method would be to create the function in the global code, outside of that anonymous function.