HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Locking viewport to the top element on window resize

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thetopresizelockingelementwindowviewport

Problem

On a responsive website where elements may shrink/expand according to the size of your viewport, the elements that was at the top of your viewport at the time may get pushed down/up as the elements above them get squished/expanded. Forcing you to scroll down/up again in order to get to the same area you were looking at.

This javascript code attempts to get rid of this problem by finding and updating the element at/near the top of the viewport on load and on scroll. Then it performs a scroll towards the current top element when the size of the viewport changes.

Based heavily on the answer I've received in my SO question for this, I got the following code in the end:

```
// Define detection area (where to pick element that we will lock on to)
var DETECT_TOP = 5,
DETECT_SIDE = 60,
DETECT_HEIGHT = 60;

// Step between each check in the detection box
// laggy if too small
var DETECT_STEP_X = 50,
DETECT_STEP_Y = 10;

// Cut-off is when the element we detected is sliced
// in-between by the top of the viewport
var CUTOFF_ADJUST = true;

var viewportWidth = $(window).width();

function Node () {
var that = this;
this.element = null;

// data
this.documentOffset = {
// jQuery element.offset().top is funny in old Safari
top: function () {
if (!that.element) { return 0; }
return that.element.offsetTop + that.element.offsetParent.offsetTop;
},
bottom: function () {
if (!that.element) { return 0; }
return this.top() + $(that.element).height();
}
};
this.viewportOffset = {
//offsets before and after resizing may be different in a responsive website
// (thinner increases its height due to word-wrap, etc.)
oldTop: 0,
oldBottom: 0,

top: function () {
this.oldTop = that.documentOffset.top() - $(window).scrollTop();
return this.oldTop;
},
bottom: function () {

Solution

I just saw this

this.viewportOffset = {
    //offsets before and after resizing may be different in a responsive website 
    // (thinner  increases its height due to word-wrap, etc.)
    oldTop:    0,
    oldBottom: 0,

    top: function () { 
        this.oldTop = that.documentOffset.top() - $(window).scrollTop();
        return this.oldTop;
    },
    bottom: function () { 
        this.oldBottom = that.documentOffset.bottom() - $(window).scrollTop();
        return this.oldBottom;
    }
};


I think that you can remove this.oldTop and this.oldBottom by writing it like this

this.viewportOffset = {
    //offsets before and after resizing may be different in a responsive website 
    // (thinner  increases its height due to word-wrap, etc.)
    top: function () { 
        return that.documentOffset.top() - $(window).scrollTop();
    },
    bottom: function () { 
        return that.documentOffset.bottom() - $(window).scrollTop();
    }
};


Less variables being declared means less memory being taken up by the website equals a faster, more responsive website.

This could be a little shorter and more to the point

this.wasBelowViewportTop = function () {
    if (this.viewportOffset.oldTop >= 0) {
        return true;
    }
    return false;
};


By using a ternary expression

this.wasBelowViewportTop = function () { this.viewportOffset.oldTop >= 0 ? true : false; };


But this is Silly as @Konijn pointed out just set it

this.wasBelowViewportTop = function () { this.viewportOffset.oldTop >= 0;};


It will evaluate the conditional and set this.wasBelowViewportTop to a true/false value.

Edit

Where you use this function can also be cleaned up by using a ternary statement

if (that.wasBelowViewportTop()) {
    scrollAmount -= that.viewportOffset.oldTop;
} else {
    scrollAmount = that.documentOffset.bottom() - that.viewportOffset.oldBottom;
}


Would become this (one line statement spread across lines for viewing)

scrollAmount = that.wasBelowViewportTop() 
    ? scrollAmount - that.viewportOffset.oldTop 
    : that.documentOffset.bottom() - that.viewportOffset.oldBottom;


Another thing that you could do is to get rid of the function altogether, you are only using it in this one place, so you could write the ternary statement like this instead

scrollAmount = this.viewportOffset.oldTop >= 0 
    ? scrollAmount - that.viewportOffset.oldTop 
    : that.documentOffset.bottom() - that.viewportOffset.oldBottom;


This lowers the code count quite a bit.

Code Snippets

this.viewportOffset = {
    //offsets before and after resizing may be different in a responsive website 
    // (thinner <p> increases its height due to word-wrap, etc.)
    oldTop:    0,
    oldBottom: 0,

    top: function () { 
        this.oldTop = that.documentOffset.top() - $(window).scrollTop();
        return this.oldTop;
    },
    bottom: function () { 
        this.oldBottom = that.documentOffset.bottom() - $(window).scrollTop();
        return this.oldBottom;
    }
};
this.viewportOffset = {
    //offsets before and after resizing may be different in a responsive website 
    // (thinner <p> increases its height due to word-wrap, etc.)
    top: function () { 
        return that.documentOffset.top() - $(window).scrollTop();
    },
    bottom: function () { 
        return that.documentOffset.bottom() - $(window).scrollTop();
    }
};
this.wasBelowViewportTop = function () {
    if (this.viewportOffset.oldTop >= 0) {
        return true;
    }
    return false;
};
this.wasBelowViewportTop = function () { this.viewportOffset.oldTop >= 0 ? true : false; };
this.wasBelowViewportTop = function () { this.viewportOffset.oldTop >= 0;};

Context

StackExchange Code Review Q#59877, answer score: 6

Revisions (0)

No revisions yet.