Michael
Michael

Reputation: 23

jQuery - piece of code causing conflict problems on pages the selector doesn't exist

Okay so I am developing a WordPress theme. On the single post page I have a comments div which floats down the page using some jquery. I am also running a modal popup form to log in. This is completely fine on the single page when the #commentWrapper (selector for the jquery floating effect) exists. However on pages where there is no #commentWrapper to float, the modal form doesn't work. I pinned down the problem to this line in my general jQuery call (by removing each line and testing).

Call in general.js, very last call:

jQuery('#commentWrapper').stickyfloat({ duration: 300, easing : 'easeInQuad' });

Actual plugin it refers to:

    $.fn.stickyfloat = function(options, lockBottom) {
            var $obj                = this;
            var parentPaddingTop    = parseInt($obj.parent().css('padding-top'));
            var startOffset         = $obj.parent().offset().top;
            var opts                = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);

            $obj.css({ position: 'absolute' });

            if(opts.lockBottom){
                var bottomPos = $obj.parent().height() - $obj.height() + parentPaddingTop; //get the maximum scrollTop value
                if( bottomPos < 0 )
                    bottomPos = 0;
            }

            $(window).scroll(function () { 
                $obj.stop(); // stop all calculations on scroll event

                var pastStartOffset         = $(document).scrollTop() > opts.startOffset;   // check if the window was scrolled down more than the start offset declared.
                var objFartherThanTopPos    = $obj.offset().top > startOffset;  // check if the object is at it's top position (starting point)
                var objBiggerThanWindow     = $obj.outerHeight() < $(window).height();  // if the window size is smaller than the Obj size, then do not animate.

                // if window scrolled down more than startOffset OR obj position is greater than
                // the top position possible (+ offsetY) AND window size must be bigger than Obj size
                if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){ 
                    var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );
                    if ( newpos > bottomPos )
                        newpos = bottomPos;
                    if ( $(document).scrollTop() < opts.startOffset ) // if window scrolled < starting offset, then reset Obj position (opts.offsetY);
                        newpos = parentPaddingTop;
                    $obj.animate({ top: newpos }, opts.duration );
                }
            });
        };

If I add an if command to see if the selector exists, it all works. I would like to know what the problem is for future website however.

Upvotes: 1

Views: 1188

Answers (1)

Fr&#233;d&#233;ric Hamidi
Fr&#233;d&#233;ric Hamidi

Reputation: 263177

Well, your stickyfloat() method assumes many things, like always being called on a jQuery object that contains at least one element, or that element always having a parent. For instance, consider the following code:

var $obj = this;
// ...
var startOffset = $obj.parent().offset().top;

If the jQuery object your method is called on is empty, or if its first element has no parent (the method was called on $("html")), the code will fail because parent().offset() will be null.

If you want your method to be more robust, you should not assume anything about the object it's called on. A good first step is to make the method chainable, which is always beneficial to your users and will get rid of the first problem. The recommended way of doing that is:

$.fn.stickyfloat = function(options, lockBottom) {
    return this.each(function() {
        var $obj = $(this);
        // The rest of your code.
    });
};

Since the code now runs sequentially on each element (if any) through an anonymous function, testing for the parent element's existence can be dealt with by returning early:

var $obj = $(this);
var $parent = $obj.parent();
if (!$parent.length) {
    return;  // No parent, continue with next element, if any.
}
// Parent element is safe to use.
var parentPaddingTop = parseInt($parent.css('padding-top'));
var startOffset = $parent.offset().top;

Upvotes: 1

Related Questions