3

I'm adding infinite scrolling to a WordPress webpage. I'm using jQuery to add a class to every third content item with the following code without any problems:

jQuery(document).ready(function($) {
    $(".leden li:nth-child(3n)").addClass('last');
});

After finding out that the above code wasn't executed after the AJAX call to show the dynamic content I've added the following code:

jQuery(document).ajaxComplete(function($) {
    $(".leden li:nth-child(3n)").addClass('last');  
});

I'm not really sure if this is the correct way to add a class with jQuery after the AJAX but it seems like it as Firebug only shows the following console error after scrolling down the page and dynamically loading the content:

TypeError: $ is not a function

This confuses me as a novice JavaScript/jQuery user because why is the error only shown for the second piece of jQuery when it uses the same format? I'm only using a different even handler as far as I know...

What am I doing wrong and also, is this the correct way to execute jQuery when using AJAX to dynamically adding content?

NielsPilon
  • 500
  • 2
  • 7
  • 21

5 Answers5

5

When you do this :

jQuery(document).ajaxComplete(function($) {

you declare a variable $ in the scope of the function, shadowing the external declaration jQuery). As you pass no argument to the function this variable has value undefined.

Just do

jQuery(document).ajaxComplete(function() {

If you want to alias jQuery as $, put your whole code in a function call like this :

(function($){
   $(document).ajaxComplete(function() {
      $(".leden li:nth-child(3n)").addClass('last');  
   });
   // the rest of your code using $ goes here
})(jQuery);
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 1
    +1 for not only showing the fix, but also describing clearly what the problem is. – GolezTrol Aug 25 '14 at 14:35
  • This solves the error in the console indeed but somehow the class isn't applied to every third item of the dynamically added content. It seems like it's applied depending on many items are loaded on scrolling down and thus triggering the infinite scrolling behaviour. – NielsPilon Aug 25 '14 at 14:51
  • 1
    `$` would be "shadowing the external declaration of jQuery" if `$` referred to jQuery in the first place, but since this is a Wordpress site, jQuery is probably loaded in noConflict mode, so `$` would simply be undefined (or could potentially be a reference to MooTools or something else). Good answer otherwise though. – Matt Browne Aug 25 '14 at 14:54
3

In response to your question, "Is this the correct way to execute jQuery when using AJAX to dynamically adding content?" I would say no, because ajaxComplete is for specifying what happens after ANY ajax result comes back, so if there's any other ajax code on your page, or if you add any plugins or anything in the future that also uses ajax, then that ajaxComplete code will run for those other ajax calls as well.

A typical ajax call would look more like this, with your additional code in the done() callback after updating the content.

$.ajax({
    type: "GET",
    url: "some.php",
    dataType: "json"
})
.done(function(data) {
    //example code to set content on the page
    $('#someDiv').html( data.someHtml );

    //your code
    $(".leden li:nth-child(3n)").addClass('last');  
});

Answer to your question about $ not working:

UPDATE

Since you said in your comment that the Ajax call is happening before document.ready, the ajaxComplete call inside of document.ready will happen too late. So just use this:

jQuery(document).ajaxComplete(function() {
    jQuery(".leden li:nth-child(3n)").addClass('last');
});

Which of course would be equivalent to this:

jQuery(document).ajaxComplete(function() {
    var $ = jQuery;
    $(".leden li:nth-child(3n)").addClass('last');
});

...but declaring $ is overkill for just a single line of code.


You probably have jQuery loaded in "no conflict" mode. When in this mode, there's a handy trick you can use to make $ available from within document.ready, which is what you're using - jQuery(document).ready(function($) { .... But you can't do that with ajaxComplete, and besides, you already have $ available if you put that code inside the document.ready callback. I'd recommend structuring your code like this:

jQuery(document).ready(function($) {
    $(document).ajaxComplete(function() {
        $(".leden li:nth-child(3n)").addClass('last');
    });
});

Or if there's no other code you need to run on document.ready, then you could just use the ajaxComplete function by itself like the other answers have suggested.

More details here: http://api.jquery.com/jquery.noconflict/

Matt Browne
  • 12,169
  • 4
  • 59
  • 75
  • 1
    Why the downvote? It's perfectly valid to use `$` as a parameter to the `document.ready` function, and in fact it's recommended to do so in the jQuery docs for noConflict mode: http://api.jquery.com/jquery.noconflict/. – Matt Browne Aug 25 '14 at 14:43
  • That is true, but you make the assumption that jQuery runs in noConflict mode which is not mentioned anywhere. The main issue is that a local scoped `$` variable (parameter) overshadows any global `$` variable if it would exist. So Conflict or noConflict, it's the parameter that needs to be fixed. The rest is speculation. Didn't downvote btw. – GolezTrol Aug 25 '14 at 14:49
  • 1
    The OP mentions it's a Wordpress site, so jQuery would be running in noConflict mode unless the OP changed it. Valid point though - the original code would still fail even if not in noConflict mode. – Matt Browne Aug 25 '14 at 14:50
  • Yes its running in noConflict mode but your solution only executes the jQuery after the Ajax call and not before unfortunately. – NielsPilon Aug 25 '14 at 14:53
  • "your solution only executes the jQuery after the Ajax call and not before unfortunately" - what do you mean not before? – Catalin MUNTEANU Aug 25 '14 at 15:02
  • You can use any $ method inside the jQuery(document).ready(function($) { // HERE }); – Catalin MUNTEANU Aug 25 '14 at 15:03
  • @NielsPilon In that case your ajax call must be happening before `document.ready` is fired. I updated my answer. – Matt Browne Aug 25 '14 at 15:03
  • Updated my answer again; you probably shouldn't be using `ajaxComplete` in the first place. I don't know all the details of your use case though. – Matt Browne Aug 25 '14 at 15:16
  • @MattBrowne thanks for your updated and very detailed answer. I'll take a look at it later on and see if I can get it working. I'm using https://github.com/paulirish/infinite-scroll for the infinite scrolling part btw. – NielsPilon Aug 25 '14 at 15:40
  • From the `infinite-scroll` docs it looks like you should put your code inside the `function(json, opts) {` callback (which probably gets passed as the `success` or `done` callback for the jQuery ajax call under the hood). – Matt Browne Aug 25 '14 at 15:44
1

Since you are running in noConflict mode in Wordpress:

jQuery(document).ready(function($) {
    // Code that uses jQuery's $ can follow here.

    $(document).ajaxComplete(function() {
        $(".leden li:nth-child(3n)").addClass('last');
    });
});

// Code that uses other library's $ can follow here.
// If there is no other library named $ then you will get a TypeError: $ is not a function

You can always do something like $ = jQuery; or use the jQuery name instead of $.

Catalin MUNTEANU
  • 5,618
  • 2
  • 35
  • 43
  • This will not work if jQuery has been loaded in [`noConflict`](http://api.jquery.com/jquery.noconflict/) mode, as is probably the case here since it's a Wordpress site and noConflict mode is the default for Wordpress. – Matt Browne Aug 25 '14 at 14:45
  • If you wanted to do it this way, you'd have to change it to `jQuery(".leden li:nth-child(3n)").addClass('last');` because `$` isn't defined. See my answer for another option. – Matt Browne Aug 25 '14 at 14:46
  • Well that's what I thought in my initial answer, but the OP said it still doesn't work. Apparently the ajax call is happening before the page has even finished loading (i.e. before `document.ready` is fired.) – Matt Browne Aug 25 '14 at 15:05
0
  • First check if $ is actually set before the call. You use jQuery instead of $ in your call so it might not be set.
  • If it is set then remove the parameter. The content of the function will have a JavaScript closure that already contains $.
Koen Peters
  • 12,798
  • 6
  • 36
  • 59
0

$ = jQuery;

So, since you're using WordPress, WordPress forces jQuery to be jQuery instead of $

Thus, your error arises because $ is not defined.

You can solve this by adding $ = jQuery right after you include the jQuery script provided that you aren't using any other libs that leverage themselves as $ (which is why WP does that in the first place) -- otherwise, you should just replace the $ with jQuery

Hope that helps you understand.

rm-vanda
  • 3,122
  • 3
  • 23
  • 34
  • *"WordPress forces jQuery to be jQuery instead of $"*. I won't affirm you're wrong, as I don't know WordPress, but are you sure of that ? Any reference ? – Denys Séguret Aug 25 '14 at 14:37
  • @dystroy Wordpress definitely loads jQuery in noConflict mode by default. There's even a question here about how to disable this behavior: http://stackoverflow.com/questions/17687619/is-there-a-way-turn-off-jquery-noconflict-mode-in-wordpress. – Matt Browne Aug 25 '14 at 14:56
  • 1
    I'm guessing the OP is writing custom code for his site, but note that if you're writing a Wordpress plugin that you intend to share with others, you should avoid this, because other sites may indeed have `$` defined as MooTools or something else. So if you're writing a plugin, it would be better to only do `$ = jQuery` within a closure, rather than "right after you include the jQuery script." – Matt Browne Aug 25 '14 at 14:58