JQuery冲突:无限滚动一次又一次地加载相同的帖子

时间:2014-08-20 14:07:45

标签: javascript jquery

JQuery无限滚动一次又一次地加载相同的帖子。这是一个JQuery冲突,但我不知道如何解决这个问题。

  /*jshint undef: true */
/*global jQuery: true */

(function (window, $, undefined) {
    "use strict";
$.infinitescroll = function infscr(options, callback, element) {
    this.element = $(element);

    // Flag the object in the event of a failed creation
    if (!this._create(options, callback)) {
        this.failed = true;
    }
};

$.infinitescroll.defaults = {
    loading: {
        finished: undefined,
        finishedMsg: "<em>No more articles.</em>",
        img: "here my image",
        msg: null,
        msgText: "<em>Loading more articles...</em>",
        selector: null,
        speed: 'fast',
        start: undefined
    },
    state: {
        isDuringAjax: false,
        isInvalidPage: false,
        isDestroyed: false,
        isDone: false, // For when it goes all the way through the archive.
        isPaused: false,
        isBeyondMaxPage: false,
        currPage: 1
    },
    debug: false,
    behavior: undefined,
    binder: $(window), // used to cache the selector
    nextSelector: "div.navigation a:first",
    navSelector: "div.navigation",
    contentSelector: null, // rename to pageFragment
    extraScrollPx: 150,
    itemSelector: "div.post",
    animate: false,
    pathParse: undefined,
    dataType: 'html',
    appendCallback: true,
    bufferPx: 40,
    errorCallback: function () { },
    infid: 0, //Instance ID
    pixelsFromNavToBottom: undefined,
    path: undefined, // Either parts of a URL as an array (e.g. ["/page/", "/"] or a function that takes in the page number and returns a URL
    prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted
    maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)
};

$.infinitescroll.prototype = {

    /*  
        ----------------------------
        Private methods
        ----------------------------
        */

    // Bind or unbind from scroll
    _binding: function infscr_binding(binding) {

        var instance = this,
        opts = instance.options;

        opts.v = '2.0b2.120520';

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {
            this['_binding_'+opts.behavior].call(this);
            return;
        }

        if (binding !== 'bind' && binding !== 'unbind') {
            this._debug('Binding value  ' + binding + ' not valid');
            return false;
        }

        if (binding === 'unbind') {
            (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);
        } else {
            (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {
                instance.scroll();
            });
        }

        this._debug('Binding', binding);
    },

    // Fundamental aspects of the plugin are initialized
    _create: function infscr_create(options, callback) {

        // Add custom options to defaults
        var opts = $.extend(true, {}, $.infinitescroll.defaults, options);
        this.options = opts;
        var $window = $(window);
        var instance = this;

        // Validate selectors
        if (!instance._validate(options)) {
            return false;
        }

        // Validate page fragment path
        var path = $(opts.nextSelector).attr('href');
        if (!path) {
            this._debug('Navigation selector not found');
            return false;
        }

        // Set the path to be a relative URL from root.
        opts.path = opts.path || this._determinepath(path);

        // contentSelector is 'page fragment' option for .load() / .ajax() calls
        opts.contentSelector = opts.contentSelector || this.element;

        // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector
        opts.loading.selector = opts.loading.selector || opts.contentSelector;

        // Define loading.msg
        opts.loading.msg = opts.loading.msg || $('<div id="infscr-loading"><img alt="Loading..." src="' + opts.loading.img + '" /><div>' + opts.loading.msgText + '</div></div>');

        // Preload loading.img
        (new Image()).src = opts.loading.img;

        // distance from nav links to bottom
        // computed as: height of the document + top offset of container - top offset of nav link
        if(opts.pixelsFromNavToBottom === undefined) {
            opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;
            this._debug("pixelsFromNavToBottom: " + opts.pixelsFromNavToBottom);
        }

        var self = this;

        // determine loading.start actions
        opts.loading.start = opts.loading.start || function() {
            $(opts.navSelector).hide();
            opts.loading.msg
            .appendTo(opts.loading.selector)
            .show(opts.loading.speed, $.proxy(function() {
                this.beginAjax(opts);
            }, self));
        };

        // determine loading.finished actions
        opts.loading.finished = opts.loading.finished || function() {
            if (!opts.state.isBeyondMaxPage)
                opts.loading.msg.fadeOut(opts.loading.speed);
        };

        // callback loading
        opts.callback = function(instance, data, url) {
            if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {
                instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);
            }

            if (callback) {
                callback.call($(opts.contentSelector)[0], data, opts, url);
            }

            if (opts.prefill) {
                $window.bind("resize.infinite-scroll", instance._prefill);
            }
        };

        if (options.debug) {
            // Tell IE9 to use its built-in console
            if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === "object") {
                ["log","info","warn","error","assert","dir","clear","profile","profileEnd"]
                    .forEach(function (method) {
                        console[method] = this.call(console[method], console);
                    }, Function.prototype.bind);
            }
        }

        this._setup();

        // Setups the prefill method for use
        if (opts.prefill) {
            this._prefill();
        }

        // Return true to indicate successful creation
        return true;
    },

    _prefill: function infscr_prefill() {
        var instance = this;
        var $window = $(window);

        function needsPrefill() {
            return (instance.options.contentSelector.height() <= $window.height());
        }

        this._prefill = function() {
            if (needsPrefill()) {
                instance.scroll();
            }

            $window.bind("resize.infinite-scroll", function() {
                if (needsPrefill()) {
                    $window.unbind("resize.infinite-scroll");
                    instance.scroll();
                }
            });
        };

        // Call self after setting up the new function
        this._prefill();
    },

    // Console log wrapper
    _debug: function infscr_debug() {
        if (true !== this.options.debug) {
            return;
        }

        if (typeof console !== 'undefined' && typeof console.log === 'function') {
            // Modern browsers
            // Single argument, which is a string
            if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {
                console.log( (Array.prototype.slice.call(arguments)).toString() );
            } else {
                console.log( Array.prototype.slice.call(arguments) );
            }
        } else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {
            // IE8
            Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
        }
    },

    // find the number to increment in the path.
    _determinepath: function infscr_determinepath(path) {

        var opts = this.options;

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {
            return this['_determinepath_'+opts.behavior].call(this,path);
        }

        if (!!opts.pathParse) {

            this._debug('pathParse manual');
            return opts.pathParse(path, this.options.state.currPage+1);

        } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
            path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);

            // if there is any 2 in the url at all.    
        } else if (path.match(/^(.*?)2(.*?$)/)) {

            // page= is used in django:
            // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
            if (path.match(/^(.*?page=)2(\/.*|$)/)) {
                path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
                return path;
            }

            path = path.match(/^(.*?)2(.*?$)/).slice(1);

        } else {

            // page= is used in drupal too but second page is page=1 not page=2:
            // thx Jerod Fritz, vladikoff
            if (path.match(/^(.*?page=)1(\/.*|$)/)) {
                path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
                return path;
            } else {
                this._debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.');
                // Get rid of isInvalidPage to allow permalink to state
                opts.state.isInvalidPage = true;  //prevent it from running on this page.
            }
        }
        this._debug('determinePath', path);
        return path;

    },

    // Custom error
    _error: function infscr_error(xhr) {

        var opts = this.options;

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {
            this['_error_'+opts.behavior].call(this,xhr);
            return;
        }

        if (xhr !== 'destroy' && xhr !== 'end') {
            xhr = 'unknown';
        }

        this._debug('Error', xhr);

        if (xhr === 'end' || opts.state.isBeyondMaxPage) {
            this._showdonemsg();
        }

        opts.state.isDone = true;
        opts.state.currPage = 1; // if you need to go back to this instance
        opts.state.isPaused = false;
        opts.state.isBeyondMaxPage = false;
        this._binding('unbind');

    },

    // Load Callback
    _loadcallback: function infscr_loadcallback(box, data, url) {
        var opts = this.options,
        callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK
        result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',
        frag;

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {
            this['_loadcallback_'+opts.behavior].call(this,box,data);
            return;
        }

        switch (result) {
            case 'done':
                this._showdonemsg();
                return false;

            case 'no-append':
                if (opts.dataType === 'html') {
                    data = '<div>' + data + '</div>';
                    data = $(data).find(opts.itemSelector);
                }
                break;

            case 'append':
                var children = box.children();
                // if it didn't return anything
                if (children.length === 0) {
                    return this._error('end');
                }

                // use a documentFragment because it works when content is going into a table or UL
                frag = document.createDocumentFragment();
                while (box[0].firstChild) {
                    frag.appendChild(box[0].firstChild);
                }

                this._debug('contentSelector', $(opts.contentSelector)[0]);
                $(opts.contentSelector)[0].appendChild(frag);
                // previously, we would pass in the new DOM element as context for the callback
                // however we're now using a documentfragment, which doesn't have parents or children,
                // so the context is the contentContainer guy, and we pass in an array
                // of the elements collected as the first argument.

                data = children.get();
                break;
        }

        // loadingEnd function
        opts.loading.finished.call($(opts.contentSelector)[0],opts);

        // smooth scroll to ease in the new content
        if (opts.animate) {
            var scrollTo = $(window).scrollTop() + $(opts.loading.msg).height() + opts.extraScrollPx + 'px';
            $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });
        }

        if (!opts.animate) {
            // once the call is done, we can allow it again.
            opts.state.isDuringAjax = false;
        }

        callback(this, data, url);

        if (opts.prefill) {
            this._prefill();
        }
    },

    _nearbottom: function infscr_nearbottom() {

        var opts = this.options,
        pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {
            return this['_nearbottom_'+opts.behavior].call(this);
        }

        this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);

        // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
        return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);

    },

    // Pause / temporarily disable plugin from firing
    _pausing: function infscr_pausing(pause) {

        var opts = this.options;

        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {
            this['_pausing_'+opts.behavior].call(this,pause);
            return;
        }
        // If pause is not 'pause' or 'resume', toggle it's value
        if (pause !== 'pause' && pause !== 'resume' && pause !== null) {
            this._debug('Invalid argument. Toggling pause value instead');
        }
        pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';
        switch (pause) {
            case 'pause':
                opts.state.isPaused = true;
            break;
            case 'resume':
                opts.state.isPaused = false;
            break;
            case 'toggle':
                opts.state.isPaused = !opts.state.isPaused;
            break;
        }
        this._debug('Paused', opts.state.isPaused);
        return false;
    },
    // Behavior is determined
    // If the behavior option is undefined, it will set to default and bind to scroll
    _setup: function infscr_setup() {
        var opts = this.options;
        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {
            this['_setup_'+opts.behavior].call(this);
            return;
        }
        this._binding('bind');
        return false;
    },
    // Show done message
    _showdonemsg: function infscr_showdonemsg() {
        var opts = this.options;
        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {
            this['_showdonemsg_'+opts.behavior].call(this);
            return;
        }
        opts.loading.msg
        .find('img')
        .hide()
        .parent()
        .find('div').html(opts.loading.finishedMsg).animate({ opacity: 1 }, 2000, function () {
            $(this).parent().fadeOut(opts.loading.speed);
        });
        // user provided callback when done    
        opts.errorCallback.call($(opts.contentSelector)[0],'done');
    },
    // grab each selector option and see if any fail
    _validate: function infscr_validate(opts) {
        for (var key in opts) {
            if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
                this._debug('Your ' + key + ' found no elements.');
                return false;
            }
        }
        return true;
    },
    /*  
        ----------------------------
        Public methods
        ----------------------------
        */
    // Bind to scroll
    bind: function infscr_bind() {
        this._binding('bind');
    },
    // Destroy current instance of plugin
    destroy: function infscr_destroy() {
        this.options.state.isDestroyed = true;
        this.options.loading.finished();
        return this._error('destroy');
    },
    // Set pause value to false
    pause: function infscr_pause() {
        this._pausing('pause');
    },
    // Set pause value to false
    resume: function infscr_resume() {
        this._pausing('resume');
    },
    beginAjax: function infscr_ajax(opts) {
        var instance = this,
            path = opts.path,
            box, desturl, method, condition;
        // increment the URL bit. e.g. /page/3/
        opts.state.currPage++;
        // Manually control maximum page 
        if ( opts.maxPage != undefined && opts.state.currPage > opts.maxPage ){
            opts.state.isBeyondMaxPage = true;
            this.destroy();
            return;
        }
        // if we're dealing with a table we can't use DIVs
        box = $(opts.contentSelector).is('table, tbody') ? $('<tbody/>') : $('<div/>');
        desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
        instance._debug('heading into ajax', desturl);

        method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';
        if (opts.appendCallback && opts.dataType === 'html') {
            method += '+callback';
        }
        switch (method) {
            case 'html+callback':
                instance._debug('Using HTML via .load() method');
                box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {
                    instance._loadcallback(box, responseText, desturl);
                });
                break;
            case 'html':
                instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
                $.ajax({
                    // params
                    url: desturl,
                    dataType: opts.dataType,
                    complete: function infscr_ajax_callback(jqXHR, textStatus) {
                        condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");
                        if (condition) {
                            instance._loadcallback(box, jqXHR.responseText, desturl);
                        } else {
                            instance._error('end');
                        }
                    }
                });
                break;
            case 'json':
                instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
                $.ajax({
                    dataType: 'json',
                    type: 'GET',
                    url: desturl,
                    success: function (data, textStatus, jqXHR) {
                        condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");
                        if (opts.appendCallback) {
                            // if appendCallback is true, you must defined template in options.
                            // note that data passed into _loadcallback is already an html (after processed in opts.template(data)).
                            if (opts.template !== undefined) {
                                var theData = opts.template(data);
                                box.append(theData);
                                if (condition) {
                                    instance._loadcallback(box, theData);
                                } else {
                                    instance._error('end');
                                }
                            } else {
                                instance._debug("template must be defined.");
                                instance._error('end');
                            }
                        } else {
                            // if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.
                            if (condition) {
                                instance._loadcallback(box, data, desturl);
                            } else {
                                instance._error('end');
                            }
                        }
                    },
                    error: function() {
                        instance._debug("JSON ajax request failed.");
                        instance._error('end');
                    }
                });
                break;
        }
    },
    // Retrieve next set of content items
    retrieve: function infscr_retrieve(pageNum) {
        pageNum = pageNum || null;
        var instance = this,
        opts = instance.options;
        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {
            this['retrieve_'+opts.behavior].call(this,pageNum);
            return;
        }
        // for manual triggers, if destroyed, get out of here
        if (opts.state.isDestroyed) {
            this._debug('Instance is destroyed');
            return false;
        }
        // we dont want to fire the ajax multiple times
        opts.state.isDuringAjax = true;
        opts.loading.start.call($(opts.contentSelector)[0],opts);
    },
    // Check to see next page is needed
    scroll: function infscr_scroll() {
        var opts = this.options,
        state = opts.state;
        // if behavior is defined and this function is extended, call that instead of default
        if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {
            this['scroll_'+opts.behavior].call(this);
            return;
        }
        if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {
            return;
        }
        if (!this._nearbottom()) {
            return;
        }
        this.retrieve();
    },
    // Toggle pause value
    toggle: function infscr_toggle() {
        this._pausing();
    },
    // Unbind from scroll
    unbind: function infscr_unbind() {
        this._binding('unbind');
    },
    // update options
    update: function infscr_options(key) {
        if ($.isPlainObject(key)) {
            this.options = $.extend(true,this.options,key);
        }
    }
};
$.fn.infinitescroll = function infscr_init(options, callback) {
    var thisCall = typeof options;
    switch (thisCall) {
        // method 
        case 'string':
            var args = Array.prototype.slice.call(arguments, 1);
            this.each(function () {
                var instance = $.data(this, 'infinitescroll');
                if (!instance) {
                    // not setup yet
                    // return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');
                    return false;
                }
                if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
                    // return $.error('No such method ' + options + ' for Infinite Scroll');
                    return false;
                }
                // no errors!
                instance[options].apply(instance, args);
            });
        break;
        // creation 
        case 'object':
            this.each(function () {
            var instance = $.data(this, 'infinitescroll');
            if (instance) {
                // update options of current instance
                instance.update(options);
            } else {
                // initialize new instance
                instance = new $.infinitescroll(options, callback, this);
                // don't attach if instantiation failed
                if (!instance.failed) {
                    $.data(this, 'infinitescroll', instance);
                }
            }
        });
        break;
    }
    return this;
};
/* 
 * smartscroll: debounced scroll event for jQuery *
 * https://github.com/lukeshumard/smartscroll
 * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js 
 * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
 */
var event = $.event,
scrollTimeout;
event.special.smartscroll = {
    setup: function () {
        $(this).bind("scroll", event.special.smartscroll.handler);
    },
    teardown: function () {
        $(this).unbind("scroll", event.special.smartscroll.handler);
    },
    handler: function (event, execAsap) {
        // Save the context
        var context = this,
        args = arguments;
        // set correct event type
        event.type = "smartscroll";
        if (scrollTimeout) { clearTimeout(scrollTimeout); }
        scrollTimeout = setTimeout(function () {
            $(context).trigger('smartscroll', args);
        }, execAsap === "execAsap" ? 0 : 100);
    }
};
$.fn.smartscroll = function (fn) {
    return fn ? this.bind("smartscroll", fn) : this.trigger("smartscroll", ["execAsap"]);
};

})(window, jQuery);

请问有人帮我吗?

0 个答案:

没有答案