jQuery scroll-prevention plugin This plugin is for article pages that has a very long main content (left side) and a shorter sidebar. When the user scrolls down mostly the right part will be white space.

What this plugin does is make the sidebar stick or make it fixed past a point and will stop sticking when it collides with the bottom part or a footer. The functionality will not work if the main content is short.

Anyway, I'm asking feedback on how to improve my JS code.

;(function ( $, window, document, undefined ) {

// Create the defaults once
var pluginName = "scrollMinUntil",
defaults = {
    article: null,
    topPadding: 0
};

// The actual plugin constructor
var Plugin = function ( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}

Plugin.prototype = {

init: function() {

    if($(this.element).length &lt;= 0)
      return null;

    this.scroll(this.element, this.options);            
},

scroll: function(el, options) {
    var article = options.article,
        topPadding = options.topPadding,
        w = $(window),
        wrap = $(el).children('.wrap'),
        startingPos = wrap.offset().top,
        fixedPosTrigger = wrap.offset().top + wrap.outerHeight(),
        stopMovingPosTrigger = 0,
        staticMarginTop = 0,
        scrollTop = 0,
        elHeight = 0,
        stopMovingPos = 0,
        fixed = false,
        staticFlag = false;

    if(options.article.outerHeight() &gt; 1500) {
      w.scroll(function() {

        // Calculate dynamic values
        scrollTop = $(this).scrollTop(),
        elHeight = wrap.outerHeight(),
        fixed = wrap.hasClass('fixed'),
        staticFlag = wrap.hasClass('static'),
        stopMovingPos = article.outerHeight() + article.offset().top;

        // Check if scrolling before or after the fixed trigger, set stopMovingPosTrigger accordingly
        (scrollTop &lt; fixedPosTrigger) ? stopMovingPosTrigger = scrollTop : stopMovingPosTrigger = scrollTop + elHeight;

        // Making sure that fixed is triggered when scrolling pass the trigger position from the original area
        if(scrollTop &gt; fixedPosTrigger &amp;&amp; !staticFlag)
          wrap
            .addClass('fixed')
            .css({
              'padding-top': topPadding + 'px'
            });

        // Fixed positioned area, setting paddings and margins, and transition scrolling from the static area
        if((scrollTop &gt; fixedPosTrigger) &amp;&amp; (stopMovingPosTrigger &lt; stopMovingPos)){
          wrap
            .removeClass('static')
            .addClass('fixed')
            .css({
              'margin-top': 0
            });
        }

        // Static positioned area, setting dynamically the margin-top for the element
        else if((stopMovingPosTrigger &gt; stopMovingPos) &amp;&amp; fixed) {

          // Calculate top margin to be added to the static element
          staticMarginTop = (stopMovingPos - startingPos) - elHeight;

          wrap
            .removeClass('fixed')
            .addClass('static')
            .css({
              'margin-top': staticMarginTop
            });
        }

        // Original area before the fixed position area
        else if(scrollTop &lt; fixedPosTrigger) {
          wrap
            .removeClass('fixed static')
            .css({
              'padding-top': 0
            });
        }
      });
    }
}
};

// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function ( options ) {
return this.each(function () {
    if (!$.data(this, "plugin_" + pluginName)) {
        $.data(this, "plugin_" + pluginName,
        new Plugin( this, options ));
    }
});
};

})( jQuery, window, document );
This part is pretty odd:


    // Calculate dynamic values
    scrollTop = $(this).scrollTop(),
    elHeight = wrap.outerHeight(),
    fixed = wrap.hasClass('fixed'),
    staticFlag = wrap.hasClass('static'),
    stopMovingPos = article.outerHeight() + article.offset().top;



The 5 assignments are separated by commas, instead of semi-colons.
It would be better to end each of the first 4 lines with a semi-colon.

This is even worse, using assignment in the right-hand sides of a ternary operator:


    (scrollTop &lt; fixedPosTrigger) ? stopMovingPosTrigger = scrollTop : stopMovingPosTrigger = scrollTop + elHeight;



It would be shorter and better to move stopMovingPosTrigger to the left, and use the ternary at the right-hand side of the assignment:

    stopMovingPosTrigger = scrollTop &lt; fixedPosTrigger ? scrollTop : scrollTop + elHeight;




Inside the scroll function, these conditions and their negated values appear multiple times:


scrollTop &gt; fixedPosTrigger
stopMovingPosTrigger &gt; stopMovingPos


To reduce the duplication which may lead to errors,
I suggest giving these conditions a name by storing them in variables, like this:

var fixedPositionArea = scrollTop &gt; fixedPosTrigger;
var staticPositionArea = stopMovingPosTrigger &gt; stopMovingPos;


This way, instead of re-typing scrollTop &gt; fixedPosTrigger multiple times,
you could use fixedPositionArea and !fixedPositionArea in your conditions.
Of course, !fixedPositionArea is not exactly the same as scrollTop &lt; fixedPosTrigger,
but it seems to me this small discrepancy won't break your script.

Using these variables the code becomes:

if (fixedPositionArea &amp;&amp; !staticFlag) {
    wrap
        .addClass('fixed')
        .css({ 'padding-top': topPadding + 'px' });
}

// Fixed positioned area, setting paddings and margins, and transition scrolling from the static area
if (fixedPositionArea &amp;&amp; !staticPositionArea) {
    wrap
        .removeClass('static')
        .addClass('fixed')
        .css({ 'margin-top': 0 });
}

// Static positioned area, setting dynamically the margin-top for the element
else if (staticPositionArea &amp;&amp; fixed) {
    // Calculate top margin to be added to the static element
    staticMarginTop = stopMovingPos - startingPos - elHeight;

    wrap
        .removeClass('fixed')
        .addClass('static')
        .css({ 'margin-top': staticMarginTop });
}

// Original area before the fixed position area
else if (!fixedPositionArea) {
    wrap
        .removeClass('fixed static')
        .css({ 'padding-top': 0 });
}




Some blocks are not indented well, for example:


var Plugin = function ( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}



It would be better to indent the body inside a { ... } block, like this:

var Plugin = function ( element, options ) {
    this.element = element;
    this.options = $.extend( {}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
};


Also, since this is an assignment, the right-hand side should be terminted with a semi-colon.

Review the rest of the code and correct all the under-indented blocks.

It would be also good to use consistent amount of indents.
In most places you seem to use 4 spaces, while in others only 2.
I suggest to pick one width and use it consistently everywhere.