I'm working on this page. Main structure is some DIVs beneath each other. I need to do some transitions or animations when user scrolls from one to another. The height of the DIVs aren't the same. it is done only by min-height:100%. My JS doesn't work when I try to do any alert at the end of the DIV.
<div id="page">
<div class="section section_1"> ...content...</div>
<div class="section section_2">...content...</div>
<div class="section section_3">...content...</div>
<div class="section section_4">...content...</div>
</div>
This is the JS file
jQuery(
$('.section').on('scroll', function () {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
alert('end of div');
}
})
);
Do you have any ideas why this doesn't work? Or can you suggest me any other solution how to make this kind of animation?
Try This:
jQuery(function($) {
$('.section').bind('scroll', function() {
if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
alert('end reached');
}
})
});
Code Example for Local
Question's already been answered here.
Edited
Bind your alert as such:
var shown = document.getElementById("page").children;
function callback () {
alert('end of div');
}
function isElementInViewport(el) {
var eap,
rect = el.getBoundingClientRect(),
docEl = document.documentElement,
vWidth = window.innerWidth || docEl.clientWidth,
vHeight = window.innerHeight || docEl.clientHeight,
efp = function (x, y) { return document.elementFromPoint(x, y) },
contains = "contains" in el ? "contains" : "compareDocumentPosition",
has = contains == "contains" ? 1 : 0x14;
// Return false if it's not in the viewport
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight)
return false;
// Return true if any of its four corners are visible
return (
(eap = efp(rect.left, rect.top)) == el || el[contains](eap) == has
|| (eap = efp(rect.right, rect.top)) == el || el[contains](eap) == has
|| (eap = efp(rect.right, rect.bottom)) == el || el[contains](eap) == has
|| (eap = efp(rect.left, rect.bottom)) == el || el[contains](eap) == has
);
}
function fireIfElementVisible (el, callback) {
return function () {
if ( isElementInViewport(el) ) {
callback();
}
}
}
var handler = fireIfElementVisible (shown[shown.length - 1], callback);
$(document).on('DOMContentLoaded load resize scroll', handler);
Above function will return boolean on whether your element is currently viewable on screen.
Related
I have a page with an iframe... Currently i used this code to load iframe onscroll....
Javascript:
function lazyLoad() {
for (var e = document.getElementsByClassName("lazy"), t = 0; t < e.length; t++) isInViewport(e[t]) && (e[t].src = e[t].getAttribute("data-src"))
}
function isInViewport(e) {
var t = e.getBoundingClientRect();
return t.bottom >= 0 && t.right >= 0 && t.top <= (window.innerHeight || document.documentElement.clientHeight) && t.left <= (window.innerWidth || document.documentElement.clientWidth)
}
function registerListener(e, t) {
window.addEventListener ? window.addEventListener(e, t) : window.attachEvent("on" + e, t)
}
registerListener("load", lazyLoad), registerListener("scroll", lazyLoad);
HTML:
<iframe data-src='http://some-link.com' src='' class='lazy'/>
But when I scroll again even if only slightly, the iframe is re-loaded again. Can anyone help me to create iframe is not loaded again after i scroll for the second time? Thank you!
DEMO: http://design-jarwo.blogspot.co.id/ and i'll use it on my blog www.kodejarwo.com
You must remove the scroll event listener after firing it:
function onScroll () {
if (lazyLoad()) {
window.removeEventListener('scroll', onScroll);
}
}
function onLoad () {
lazyLoad();
}
function lazyLoad() {
var loaded = false;
for (var e = document.getElementsByClassName("lazy"), t = 0; t < e.length; t++) {
isInViewport(e[t]) && (e[t].src = e[t].getAttribute("data-src"));
loaded = true;
}
return loaded;
}
function isInViewport(e) {
var t = e.getBoundingClientRect();
return t.bottom >= 0 && t.right >= 0 && t.top <= (window.innerHeight || document.documentElement.clientHeight) && t.left <= (window.innerWidth || document.documentElement.clientWidth)
}
function registerListener(e, t) {
window.addEventListener ? window.addEventListener(e, t) : window.attachEvent("on" + e, t)
}
registerListener("load", onLoad), registerListener("scroll", onScroll);
What is the most reliable and efficient way to find all elements having a scroll on a page?
Currently, I'm thinking about using element.all() with filter() comparing the height and scrollHeight attribute values:
element.all(by.xpath("//*")).filter(function (elm) {
return protractor.promise.all([
elm.getAttribute("height"),
elm.getAttribute("scrollHeight")
]).then(function (heights) {
return heights[1] > heights[0];
});
});
But I'm not sure about the correctness and performance of this approach.
This works with both horizontal and vertical scrollbars. The trick is detecting BOTH the too-wide/too-short AND if the computed CSS is going to allow you to display a scrollbar.
var ElementsWithScrolls = (function() {
var getComputedStyle = document.body && document.body.currentStyle ? function(elem) {
return elem.currentStyle;
} : function(elem) {
return document.defaultView.getComputedStyle(elem, null);
};
function getActualCss(elem, style) {
return getComputedStyle(elem)[style];
}
function isXScrollable(elem) {
return elem.offsetWidth < elem.scrollWidth &&
autoOrScroll(getActualCss(elem, 'overflow-x'));
}
function isYScrollable(elem) {
return elem.offsetHeight < elem.scrollHeight &&
autoOrScroll(getActualCss(elem, 'overflow-y'));
}
function autoOrScroll(text) {
return text == 'scroll' || text == 'auto';
}
function hasScroller(elem) {
return isYScrollable(elem) || isXScrollable(elem);
}
return function ElemenetsWithScrolls() {
return [].filter.call(document.querySelectorAll('*'), hasScroller);
};
})();
ElementsWithScrolls();
It will select the elements with overflowed and non-overflowed scrolls inside body tag:
$('body *').filter(function() {
return ($(this).scrollTop() != 0 || $(this).css('overflow') == 'scroll');
});
A link to the Plunker.
I've been working on a button and list system in jQuery for awhile now. Recently I decided to make my code more reproducible. I want to make it so I just have to add classes or IDs, and I don't have to add any additional code. I'm very close to doing that for my entire site. So if you go to this site specifically you will see it in action.
If you click on any buttons, in any order, it will arrange chronologically.
The bugs come from closing them.
If you click at least three, close the middle one, then click a new button, the sort function falls apart and that closed middle one is now floating with the wrong class.
Below is my current jQuery. On my site, ignore the "All Years" button. I'll work on that after I figure out this bug.
//the variables needed for the floating buttons
var groupArray = $(".yearGroup");
var buttonArray = $(".buttonGroup");
var hideGroupArray = $(".hideGroup");
var closeBarArray = $(".closeBar");
var closeBar = $("#allCloseBar");
var allButtonArray = [];
sortElements = function(a,b)
{
if (a.text() < b.text())
{
return -1;
}
else if (a.text() > b.text())
{
return 1;
}
else
{
return 0;
}
}
$.each(buttonArray, function(i, item) {
$(this).click(function(){
console.log($(buttonArray[i]).text())
console.log($(closeBarArray[i]).text())
//for removing the tooltip when the button is clicked. Mostly for Firefox bug
$(".ui-tooltip-content").parents('div').remove();
$(hideGroupArray[i-1]).slideToggle(slideToggleDuration, function(){
htmlBody.animate({scrollTop: $(groupArray[i-1]).offset().top - 25}, {duration: timeDuration, easing: 'easeOutBack'});
$(buttonArray[i]).toggleClass("float", 1200);
if ($(groupArray[i-1]).height() > 0)
{
//This will stop any animations if the user scrolls.
htmlBody.bind("scroll mousedown DOMMouseScroll mousewheel keyup", function(e)
{
if ( e.which > 0 || e.type === "mousedown" || e.type === "mousewheel"){
htmlBody.stop().unbind('scroll mousedown DOMMouseScroll mousewheel keyup');
}
});
closeBar.addClass("floatCloseBar");
$(closeBarArray[i]).hide();
allButtonArray.splice(0, 0, $(buttonArray[i]));
var timer;
var delay = 1500;
$(buttonArray[i]).hover(function() {
//This will stop any animations if the user scrolls.
htmlBody.bind("scroll mousedown DOMMouseScroll mousewheel keyup", function(e)
{
if ( e.which > 0 || e.type === "mousedown" || e.type === "mousewheel"){
htmlBody.stop().unbind('scroll mousedown DOMMouseScroll mousewheel keyup');
}
});
var link = $(groupArray[i-1]);
var offset = link.offset();
var top2 = offset.top;
var left = offset.left;
var bottom = top2 + $(groupArray[i-1]).outerHeight();
//bottom = Math.abs(bottom - offset.top);
var right = $(window).width() - link.width();
right = Math.abs(offset.left - right);
var scrollDuration = 0;
if (inRange($(buttonArray[i]).offset().top, $(groupArray[i-1]).position().top, bottom))
{
//console.log("fast");
scrollDuration = 500;
//$(group).addClass("hoverYear");
}
else if ($(buttonArray[i]).offset().top <= $(groupArray[i-1]).offset().top && allButtonArray.length == 1)
{
//console.log("fast");
scrollDuration = 500;
//$(group).removeClass("hoverYear");
}
else if ($(buttonArray[i]).offset().top > 495 && $(buttonArray[i]).offset().top < 1700 && !inRange($(buttonArray[i]).offset().top, $(groupArray[i-1]).position().top, bottom))
{
scrollDuration = 1000;
//console.log("slow");
//$(group).removeClass("hoverYear");
}
else if ($(buttonArray[i]).offset().top > 1701 && $(buttonArray[i]).offset().top < 3000 && !inRange($(buttonArray[i]).offset().top, $(groupArray[i-1]).position().top, bottom))
{
scrollDuration = 1500;
//console.log("slower");
//$(group).removeClass("hoverYear");
}
else if ($(buttonArray[i]).offset().top > 3001 && $(buttonArray[i]).offset().top < 6000 && !inRange($(buttonArray[i]).offset().top, $(groupArray[i-1]).position().top, bottom))
{
scrollDuration = 2000;
//console.log("much slower");
//$(group).removeClass("hoverYear");
}
else if ($(buttonArray[i]).offset().top > 6001 && !inRange($(buttonArray[i]).offset().top, $(groupArray[i-1]).position().top, bottom))
{
scrollDuration = 2500;
console.log("the slowest");
//$(group).removeClass("hoverYear");
}
else
{
scrollDuration = 500;
}
//to prevent the various hover states to take control when the button isn't floating
if (!($(buttonArray[i])).hasClass("float"))
{
scrollDuration = 0;
console.log("doesnt have class")
}
// on mouse in, start a timeout
timer = setTimeout(function() {
//the delay for the hover scroll feature
htmlBody.animate({scrollTop: $(groupArray[i-1]).offset().top}, scrollDuration, 'easeInOutCubic');
}, delay);
}, function() {
// on mouse out, cancel the timer
clearTimeout(timer);
});
$.each(allButtonArray, function(j, val){
$(allButtonArray[j]).appendTo(closeBar);
console.log(allButtonArray.length);
arrowDown.show();
arrowUp.show();
arrowDown.prependTo(closeBar);
arrowUp.appendTo(closeBar);
//Changes the width of the buttons based upon how many are on the screen
if (allButtonArray.length > 7)
{
$("float").css('width', '7%');
$(val).css('width', '7%');
$(allButtonArray[0]).css('width','7%');
allButtonArray.sort(sortElements);
//console.log(val);
}
else if (allButtonArray.length <= 7)
{
$(val).css("width", '10%');
$("float").css("width", '10%');
allButtonArray.sort(sortElements);
//console.log(val);
}
});
}
if ($(groupArray[i-1]).height() == 0)
{
$(buttonArray[i]).css("width", '50%');
allButtonArray.splice(allButtonArray.indexOf($(buttonArray[i])), 1);
console.log(allButtonArray.length);
$(closeBarArray[i]).show();
$(buttonArray[i]).appendTo($(closeBarArray[i]));
arrowDown.show();
arrowUp.show();
arrowDown.prependTo(closeBar);
arrowUp.appendTo(closeBar);
}
if (group2001.height() == 0 && group2002.height() == 0 && group2003.height() == 0 && group2004.height() == 0 && group2005.height() == 0 && group2006.height() == 0 && group2007.height() == 0
&& group2008.height() == 0 && group2009.height() == 0 && group2010.height() == 0 && group2011.height() == 0 && group2012.height() == 0)
{
$(closeBarArray[i]).removeClass("floatCloseBar");
htmlBody.animate({scrollTop: revealAllButton.offset().top - 75}, 500);
arrowDown.hide();
arrowUp.hide();
//console.log($(document).height() + " the current height");
}
});
$(buttonArray[i]).toggleClass("openClose");
$(buttonArray[i]).toggleClass("openClose2");
});
});
function inRange(x, min, max){
return (x >= min && x <= max);
}
If you would like a reference to what worked previously, I could post that code. It is much more bulky and much less organized. I've tried many different things to eliminate the bug but I'm at a loss. My knowledge of JS scope is limited.
And thanks for any help, it is very much appreciated.
I'm facing some problem to scroll the HTML sections with the help of viewport jQuery plugin. I have 4 sections on a page, I want to scroll one by one while mouse scrolling (up or down). My HTML and JavaScript code are given below:
<section id="chapter1-block" class="chapter">
<span id="chapter1" class="pointer"></span><span class="pointer-b"></span>
</section>
<section id="chapter2-block" class="chapter">
<span id="chapter2" class="pointer"></span><span class="pointer-b"></span>
</section>
<section id="chapter3-block" class="chapter">
<span id="chapter3" class="pointer"></span><span class="pointer-b"></span>
</section>
<section id="chapter4-block" class="chapter">
<span id="chapter4" class="pointer"></span><span class="pointer-b"></span>
</section>
JS:
$(function () {
var _top = $(window).scrollTop();
var _direction;
var curChapterPos = 'chapter1';
$(window).scroll(function(){
var _cur_top = $(window).scrollTop();
console.log(curChapterPos);
if(_top < _cur_top)
{
if(curChapterPos == "chapter1")
{
$('body').scrollTo( '#chapter2');
curChapterPos = 'chapter2';
console.log(_cur_top);
return false;
}
else
if(curChapterPos == "chapter2")
{
$('body').scrollTo( '#chapter2');
curChapterPos = 'chapter3';
console.log('3--'+curChapterPos);
return false;
}else
if(curChapterPos == "chapter3")
{
$('body').scrollTo( '#chapter3');
curChapterPos = 'chapter4';
console.log('3--'+curChapterPos);
return false;
}
_direction = 'down';
}
else
{
_direction = 'up';
}
_top = _cur_top;
});
});
The problem here, is when the page scrolls, the functions is invoked 7 times if you use the scroll bar from the browser. 9 times with the key down. And If you use the mouse wheel the function is invoked between 12 and 25 times. So, the problem could be solved if you avoid the use of the mouse, because the sensibility of the mouse could be different in any client/browser. But this not resolve the original question. Maybe if combine the page scroll with the mouse hover and the visibility of the tag, will result in the answer you need.
Here is my partial solution. It makes a loop if the mouse scroll goes down.
http://jsfiddle.net/manueru_mx/Wm6Pn/
$(function () {
var _top = $(window).scrollTop();
$("#xtop").val(0);
var _direction;
var curChapterPos = '';
var _last = false;
$(window).scroll(function(){
var _cur_top = $(window).scrollTop();
var _top = $("#xtop").val();
curChapterPos = $("#chapterhidden").val();
if(_top < _cur_top){
if(curChapterPos == "chapter1"){
$('body').scrollTo( '#chapter2');
curChapterPos = 'chapter2';
$("#chapterhidden").val(curChapterPos);
}else if(curChapterPos == "chapter2"){
$('body').scrollTo( '#chapter2');
curChapterPos = 'chapter3';
$("#chapterhidden").val(curChapterPos);
}else if(curChapterPos == "chapter3"){
$('body').scrollTo( '#chapter3');
curChapterPos = 'chapter4';
$("#chapterhidden").val(curChapterPos);
} else if(curChapterPos == "chapter4" && elementInViewport(document.getElementById('chapter3')) == false){
console.log("aquisss");
$('body').scrollTo( '#chapter4');
curChapterPos = 'chapter4';
$("#chapterhidden").val(curChapterPos);
$("#xtop").val(_cur_top+1000);
}else{
return false;
}
_direction = 'down';
$("#xtop").val(_cur_top);
}else {
if(curChapterPos == "chapter4" && elementInViewport(document.getElementById('chapter4-block')) == false){
$('body').scrollTo( '#chapter3');
curChapterPos = 'chapter3';
$("#chapterhidden").val(curChapterPos);
} else if (curChapterPos== "chapter3"){
$('body').scrollTo( '#chapter2');
curChapterPos = 'chapter2';
$("#chapterhidden").val(curChapterPos);
} else if (curChapterPos== "chapter2"){
$('body').scrollTo( '#chapter1');
curChapterPos = 'chapter1';
$("#chapterhidden").val(curChapterPos);
}
_direction = 'up';
$("#xtop").val(_cur_top);
}
});
});
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top >= window.pageYOffset &&
left >= window.pageXOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) &&
(left + width) <= (window.pageXOffset + window.innerWidth)
);
}
When you enter the last position, although the direction of travel is up, never enters the condition. So I add 1000 to the end position. This creates the cycle and lets start from the beginning. It is wrong, but i can not find logic in behavior. Interestingly only happens in the last position.
Tested on FF 16 and IE 8 with compatibility view. and works with mouse scroll, key down and browser scroll bar.
Using this plugin would be easier.
http://www.appelsiini.net/projects/viewport
But I would not change much your initial approach. I used the library scrollTo because I see you use that function and is not native to jQuery
http://demos.flesler.com/jquery/scrollTo/js/jquery.scrollTo-min.js
Hope this help
References:
ElementInViewPort function
Can one use Window.Onscroll method to include detection of scroll direction?
If you record the scrollX and scrollY on page load and each time a scroll event occurs, then you can compare the previous values with the new values to know which direction you scrolled. Here's a proof of concept:
function scrollFunc(e) {
if ( typeof scrollFunc.x == 'undefined' ) {
scrollFunc.x=window.pageXOffset;
scrollFunc.y=window.pageYOffset;
}
var diffX=scrollFunc.x-window.pageXOffset;
var diffY=scrollFunc.y-window.pageYOffset;
if( diffX<0 ) {
// Scroll right
} else if( diffX>0 ) {
// Scroll left
} else if( diffY<0 ) {
// Scroll down
} else if( diffY>0 ) {
// Scroll up
} else {
// First scroll event
}
scrollFunc.x=window.pageXOffset;
scrollFunc.y=window.pageYOffset;
}
window.onscroll=scrollFunc
With jquery, you can also register a custom scroll event which supplies the scroll change as an argument to the event handler:
var previous_scroll = $(window).scrollTop();
$(window).on('scroll', function() {
var scroll = $(window).scrollTop(),
scroll_change = scroll - previous_scroll;
previous_scroll = scroll;
$(window).trigger('custom_scroll', [scroll_change]);
});
Then instead of scroll, bind to custom_scroll:
$(window).on('custom_scroll', function pos(e, scroll_change) {
console.log(scroll_change);
});
I had trouble making this work in ie8 (although it is compliant for ie9, FF and Chrome) - all scrolls seem to be detected as horizontal.
Here is a modified script demo that also works in ie8 and may cover a few more browsers.
function scrollFunc(e) {
function getMethod() {
var x = 0, y = 0;
if ( typeof( window.pageYOffset ) == 'number' ) {
x = window.pageXOffset;
y = window.pageYOffset;
}
else if( document.body && (document.body.scrollLeft || document.body.scrollTop ) ) {
x = document.body.scrollLeft;
y = document.body.scrollTop;
}
else if( document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
x = document.documentElement.scrollLeft;
y = document.documentElement.scrollTop;
}
return [x, y];
}
var xy = getMethod();
var xMethod = xy[0];
var yMethod = xy[1];
if ( typeof scrollFunc.x == 'undefined' ) {
scrollFunc.x = xMethod;
scrollFunc.y = yMethod;
}
var diffX = scrollFunc.x - xMethod;
var diffY = scrollFunc.y - yMethod;
if( diffX<0 ) {
// Scroll right
} else if( diffX>0 ) {
// Scroll left
} else if( diffY<0 ) {
// Scroll down
} else if( diffY>0 ) {
// Scroll up
} else {
// First scroll event
}
scrollFunc.x = xMethod;
scrollFunc.y = yMethod;
}
window.onscroll=scrollFunc​