简体   繁体   中英

jQuery Not Defined within a function

Alright, here's a puzzler. I've got a jQuery function to display a PHP generated list of announcements for a website via .fadeIn/.fadeOut; the very first thing loaded on the page is jQuery 1.11.xx from a CDN. I'm running Bootstrap, fullCalendar, SmartMenus, etc., and jQuery is most definitely loading.

Except within the setInterval() to update the announcement. This is rough-code, some functionality isn't present, but to my mind it should be doing an animation.

var announcementArray = [];
var announcementSource = "../announcements.php";
var totalAnnc;

$.getJSON(announcementSource, function(data) {
    announcementArray = data.concat();
    totalAnnc = announcementArray.length;
});

var count = 0;

var fadeAnnc = function() {

    $('#announcementArea').text(announcementArray[count].announceText);
    $('#announcementArea').fadeIn('slow',function() {
        $('#announcementArea').css('display','block');
    }).delay(2000).fadeOut('slow',function() {
        count = (count + 1) % totalAnnc;
    });
};

setInterval(function() {
    fadeAnnc();
}, 3000);   

Instead, when I run the page, I get a "function not defined" error for any jQuery function that's called within the setInterval(). If I call using document.getElementById('announcementArea').innerHTML = etc., it works, but doing the fade in/out via DOM manipulation seems to be more work than is needed when jQuery is available and working everywhere else on the page.

I've tried a few scope adjustments and have been working on what should be simple code for the last 5 hours. So, where's my glaring error? ;)

Not sure what kind of scope issue you are having (looks like it's the result of unposted code, as everything in your question looks OK), but if you want a fairly foolproof way of passing along the jQuery object, you could always pass it as a parameter:

var fadeAnnc = function($) {

    $('#announcementArea').text(announcementArray[count].announceText);
    $('#announcementArea').fadeIn('slow',function() {
        $('#announcementArea').css('display','block');
        }).delay(2000).fadeOut('slow',function() {
        count = (count + 1) % totalAnnc;
    });
};

setInterval(function() {
    fadeAnnc($);
}, 3000);   

Based on your updated answer, here's another possible solution:

(function($){

    var announcementArray = [];
    var announcementSource = "../announcements.php";
    var announcementSpace = "#announcementArea";


    $.getJSON(announcementSource, function(data) {
        announcementArray = data.concat();
        if (announcementArray.length === 0) {
            $('#anncRow').css('display','none');
        }
    });

    var count = 0;
    var masterCount = 0;
    var totalAnnc =  announcementArray.length;
    var timer;

    var fadeAnnc = function() {
        if (announcementArray.length > 0) {
            $(announcementSpace).html(announcementArray[count].announceText);
            $(announcementSpace).fadeIn(750, function() {
                $(announcementSpace).css('display','block');
            }).delay(4500).fadeOut(750, function() {
                $(announcementSpace).css('display','hidden');
            });
        }
        count += 1;
        if ((count % announcementArray.length) == 0) {count = 0}
    };

    setInterval(fadeAnnc, 6000);    

}(jQuery));

$ is defined as a function parameter and thus overrides the globally scoped $ within the function body, protecting it's definition for your code. This is actually exactly what jQuery recommends when creating an extension .

My previous answer - scratch that:

The issue was more interesting - somewhere between the SmartMenu plugin and the LibraryThing book display widget there is a jQuery conflict created. This explains why - depending on the load order - different parts would break, but always the setInterval(), which always loaded after SmartMenu and LibraryThing.

So, my somewhat messy solution is to release the $ at the beginning of the script and reclaim it at the end so on other pages jQuery has access to it, like so:

jq = jQuery.noConflict();

var announcementArray = [];
var announcementSource = "../announcements.php";
var announcementSpace = "#announcementArea";


jq.getJSON(announcementSource, function(data) {
    announcementArray = data.concat();
    if (announcementArray.length === 0) {
        jq('#anncRow').css('display','none');
    }
});

var count = 0;
var masterCount = 0;
var totalAnnc =  announcementArray.length;
var timer;

var fadeAnnc = function() {
    if (announcementArray.length > 0) {
        jq(announcementSpace).html(announcementArray[count].announceText);
        jq(announcementSpace).fadeIn(750, function() {
            jq(announcementSpace).css('display','block');
        }).delay(4500).fadeOut(750, function() {
            jq(announcementSpace).css('display','hidden');
        });
    }
    count += 1;
    if ((count % announcementArray.length) == 0) {count = 0}
};

setInterval(fadeAnnc, 6000);    

$ = jQuery.noConflict();

Use closures (which is considered good practice anyways):

(function($) {

    var your_function = function() {
        $(...);
    };

    setTimeout(function() {
        your_function();
    });

}(jQuery));

Using closures creates a sort of 'sandbox' for your code, so you don't have to worry about overwriting any variables declared in a parent scope (such as the dollar-sign $ used by jQuery).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM