Skip to content

Instantly share code, notes, and snippets.

@madr
Last active December 31, 2015 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madr/7987137 to your computer and use it in GitHub Desktop.
Save madr/7987137 to your computer and use it in GitHub Desktop.
Primitive scroll guard utility
/*jslint browser: true, indent: 4 */
/*
Example use of ScrollGuard: a simple, unobtrusive lazy loader.
Each time the user scrolled to the bottom of the page:
1. Look if there is content to load (lookForNextPage)
2. If new content, get content using Ajax (getNextPage)
3. Append the new content and do some polish (appencContent)
*/
(function (g) {
"use strict";
var lookForNextPage,
getNextPage,
appendContent;
if (g.ScrollGuard === undefined) {
throw new Error("scrollguard.js is required!");
}
appendContent = function (content) {
var body, rows, i, max, newContent, table;
body = document.createElement("div");
body.innerHTML = content;
newContent = document.createDocumentFragment();
rows = body.querySelectorAll(".main-c table tr");
for (i = 1, max = rows.length; i < max; i += 1) {
newContent.appendChild(rows[i]);
}
table = document.querySelector(".main-c table").tBodies[0];
table.replaceChild(newContent, table.querySelector("tr.pagination"));
};
lookForNextPage = function () {
var nextPage = document.getElementById("pager-next");
if (nextPage) {
getNextPage(nextPage.href);
}
};
getNextPage = function (href) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200 || request.status === 304) {
appendContent(request.responseText);
}
}
};
request.open("GET", href, true);
request.send(null);
};
if (document.getElementById("pager-next")) {
g.ScrollGuard.add(lookForNextPage, -1);
g.ScrollGuard.start();
}
}(this));
/*jslint browser: true, indent: 4 */
/*global updateCanvas: true */
(function (g) {
"use strict";
var lookForNextPage,
getNextPage,
appendContent,
loadManually,
manual,
loader,
working;
if (g.ScrollGuard === undefined) {
throw new Error("scrollguard.js is required!");
}
appendContent = function (content) {
var body, rows, i, max, newContent, table;
body = document.createElement("div");
body.innerHTML = content;
newContent = document.createDocumentFragment();
rows = body.querySelectorAll(".main-c table tr");
for (i = 1, max = rows.length; i < max; i += 1) {
newContent.appendChild(rows[i]);
}
table = document.querySelector(".main-c table").tBodies[0];
table.replaceChild(newContent, table.querySelector("tr.pagination"));
if (typeof(updateCanvas) === "function") {
updateCanvas();
}
loadManually();
};
loadManually = function () {
var button;
button = document.getElementById("load-manually");
if (button) {
if (!document.getElementById("pager-next")) {
button.parentNode.removeChild(button);
}
button.onclick = function () {
var nextPage = document.getElementById("pager-next");
if (nextPage) {
getNextPage(nextPage.href);
button.parentNode.replaceChild(loader.cloneNode(), button);
}
};
}
};
manual = function () {
var style, elm;
elm = document.getElementById("load-manually");
if (!elm) { return false; }
style = window.getComputedStyle(elm, null);
return style.getPropertyValue("display") !== "none";
};
lookForNextPage = function () {
if (!manual()) {
var nextPage = document.getElementById("pager-next");
if (nextPage) {
getNextPage(nextPage.href);
}
}
};
getNextPage = function (href) {
var request = new XMLHttpRequest();
if (working === true) {
return;
}
working = true;
request.onreadystatechange = function () {
if (request.readyState === 4) {
working = false;
if (request.status === 200 || request.status === 304) {
appendContent(request.responseText);
}
}
};
request.open("GET", href, true);
request.send(null);
};
loadManually();
loader = document.createElement("img");
loader.style.paddingTop = "14px";
loader.src = "/templates/wide/css/images/loader.gif";
if (document.getElementById("pager-next")) {
g.ScrollGuard.add(lookForNextPage, -1);
g.ScrollGuard.start();
}
}(this));
/*jslint browser: true, indent: 4 */
(function (g) {
"use strict";
var ScrollGuard,
started = false,
lock = false,
timer,
breakpoints = [],
getDocHeight,
getScrollTop;
// found at http://james.padolsey.com/javascript/
// get-document-height-cross-browser/
getDocHeight = function () {
return Math.max(
Math.max(document.body.scrollHeight,
document.documentElement.scrollHeight),
Math.max(document.body.offsetHeight,
document.documentElement.offsetHeight),
Math.max(document.body.clientHeight,
document.documentElement.clientHeight)
);
};
// found at http://stackoverflow.com/questions/871399/
// cross-browser-method-for-detecting-the-scrolltop-of-the-browser-g
getScrollTop = function () {
var b, d;
if (g.pageYOffset !== undefined) {
return g.pageYOffset;
}
b = document.body;
d = document.documentElement;
d = (d.clientHeight) ? d : b;
return d.scrollTop;
};
ScrollGuard = {
// from: a distance from top, given in pixels (required)
// to: a distance from top, given in pixels (optional)
//
// special cases:
// from = -1 will instead look if bottom of page
// is visible.
add: function (callback, from, to) {
var range;
if (from === -1) {
range = function (topPos) {
return getDocHeight() - topPos <= g.innerHeight;
};
} else {
if (to === undefined) {
range = function (topPos) {
return topPos >= from;
};
} else {
range = function (topPos) {
return topPos >= from && topPos <= to;
};
}
}
breakpoints.push({
within: range,
callback: callback
});
},
fire: function (pos) {
var max = breakpoints.length,
i,
bp;
lock = true;
for (i = 0; i < max; i += 1) {
bp = breakpoints[i];
if (bp.within(pos)) {
bp.callback();
}
}
lock = false;
},
start: function () {
if (started) { return; }
var callback = function () {
if (lock === false) {
clearTimeout(timer);
timer = setTimeout(function () {
ScrollGuard.fire(getScrollTop());
}, 100);
}
};
if (g.attachEvent) {
g.attachEvent('onscroll', callback);
} else {
g.addEventListener('scroll', callback, false);
}
started = true;
// initial call to catch initial scroll positions
callback();
}
};
g.ScrollGuard = ScrollGuard;
}(this));
@madr
Copy link
Author

madr commented Dec 16, 2013

TODO

  • ScrollGuard.remove
  • repeat support (with automatic detach)
  • threshold support
  • Tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment