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
Related
I'm creating a game where the user wanders around a cemetery and collects stories from different graves. It's a classic top-down game. I'm building a script where if the user walks into a grave their movement stops, but I'm having trouble setting up collisions. I am using jQuery. Here is what I have so far:
var position = -1;
var $char = $('#char');
var keyCode = null;
var fired = false;
var $stones = $('.stones div');
var collision = null;
document.onkeydown = function(e) {
keyCode = e.which || e.keyCode;
if (!fired) {
position = -1;
fired = true;
switch (keyCode) {
case 38: position = 0; break; //up
case 40: position = 1; break; //down
case 37: position = 2; break; //left
case 39: position = 3; break; //right
}
walking();
stepping = setInterval(walking,125);
}
};
document.onkeyup = function(e) {
//standing
clearInterval(stepping);
stepping = 0;
fired = false;
};
function walking() {
$stones.each(function() { //check all the stones...
collision = collision($(this), $char, position); ...for collisions
if (collision) { //if any, then break loop
return false;
}
});
if (!collision) { //check if there was a collision
//if no collision, keep walking x direction
}
function collision($el, $charEl, position) {
var $el = $el[0].getBoundingClientRect();
var $charEl = $charEl[0].getBoundingClientRect();
var elBottom = parseInt($el.bottom);
var elRight = parseInt($el.right);
var elLeft = parseInt($el.left);
var elTop = parseInt($el.top);
var charBottom = parseInt($charEl.bottom);
var charRight = parseInt($charEl.right);
var charLeft = parseInt($charEl.left);
var charTop = parseInt($charEl.top);
//this is where I'm stuck
}
}
I've tried various different codes, but nothing seems to work. I keep having an issue where if I'm going forward and then I bump into a headstone and I turn around, I'm stuck. Here's an example code of what I mean:
if (position == 0 &&
!(elTop > charBottom ||
elBottom < charTop ||
elRight < charLeft + 1 ||
elLeft > charRight - 1)
) {
return true;
}
if (position == 1 &&
!(elTop > charBottom ||
elBottom < charTop ||
elRight < charLeft + 1 ||
elLeft > charRight - 1)
) {
return true;
}
return false;
I have looked this question and this question and this question and so far I'm not having any luck. Can somebody help me with the logic or supply an example code of what I need to do?
Thank you.
Your game is looking good man!
I recently wrote some collision detection and had the exact same problem. The issue is that once your coordinates are true of the case of the collision then they will always be true on any other movement.
You need to store the previous position your character was in and revert back to it OR perform the check before you change your characters coordinates.
I managed to find the following solution, thanks to stwitz' about idea, as well as this script: https://magently.com/blog/detecting-a-jquery-collision-part-iv/
var position = -1;
var $char = $('#char');
var keyCode = null;
var fired = false;
var stepSize = 32;
var $stones = $('.stones div');
//new
var cancelTop = cancelRight = cancelLeft = cancelBottom = false;
var charEl = $char[0].getBoundingClientRect();
var charLeft = parseInt(charEl.left);
var charRight = parseInt(charEl.right);
var charTop = parseInt(charEl.top);
var charBottom = parseInt(charEl.bottom);
function walking() {
if (position == 0 && !cancelTop) {
//if moving up & is safe to move up
} else if (position == 1 && !cancelBottom) {
//if moving down & is safe to move down
} else if (position == 2 && !cancelLeft) {
//if moving left and is safe to move left
} else if (position == 3 && !cancelRight) {
//if moving right and is safe to move right
}
cancelTop = cancelRight = cancelLeft = cancelBottom = false; //mark all as safe until we check
$stones.each(function() {
collision($(this));
});
}
document.onkeydown = function(e) {
keyCode = e.which || e.keyCode;
if (!fired) {
position = -1;
fired = true;
switch (keyCode) {
case 38: position = 0; break; //up
case 40: position = 1; break; //down
case 37: position = 2; break; //left
case 39: position = 3; break; //right
}
walking();
stepping = setInterval(walking,125);
}
};
document.onkeyup = function(e) {
//standing
clearInterval(stepping);
stepping = 0;
fired = false;
};
function collision($el) {
var el = $el[0].getBoundingClientRect();
var elBottom = parseInt(el.bottom);
var elRight = parseInt(el.right);
var elLeft = parseInt(el.left);
var elTop = parseInt(el.top);
if (
(elRight == charLeft) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelLeft = true;
return true;
}
if (
(elLeft == charRight) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelRight = true;
return true;
}
if (
(elTop + stepSize > charBottom) &&
(elTop <= charBottom) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelBottom = true;
return true;
}
if (
(elBottom - stepSize < charTop) &&
(elBottom >= charTop) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelTop = true;
return true;
}
return false;
}
I'm trying to add a side navigation bar to my website, which animates in from the right hand side when the user has scrolled so that the normal navigation bar isnt visible. Ive managed to get it so its animating how I want but not when i want. It just alternates between being shown, and not being shown whenever the user scrolls, not whenever the user scrolls past the header.
I am using jquery and here is a jsfiddle:
http://jsfiddle.net/2twmcmzh/1/ and my current js:
$(document).ready(function() {
var $header = $('header');
var $sideButtons = $('.roundSideButton');
var scrollstate = 'hidden';
var animating = false;
var duration = 500;
$( window ).scroll(function() {
var scrollToTop = $(window).scrollTop();
if(animating === false){
if(scrollToTop > $('header').height() && scrollstate == 'hidden'){
showSideButtons(true);
}else if (scrollstate == 'shown' ){
showSideButtons(false);
}
}
});
function showSideButtons(hide){
animating = true;
if(hide){
$.each($sideButtons,function(i) {
$(this).stop().delay(i * (duration / 2)).animate({right:"20px"}, duration,function(){animating = false;});
});
setTimeout(function() {
animating = false;
scrollstate = 'shown';
console.log(" " + scrollstate + " " + animating);
}, duration * $sideButtons.length);
}else{
$.each($sideButtons,function(i) {
$(this).stop().delay(i * (duration / 2)).animate({right:"-20px"}, duration);
});
setTimeout(function() {
animating = false;
scrollstate = 'hidden';
console.log(" " + scrollstate + " " + animating);
}, duration * $sideButtons.length);
}
}
});
If I understand you correct, everything OK when the user scroll down. Whenever that the header is hidden the points are shown. (From I see in the fiddle). The problem start when the user scroll to the top, than the point are hidden before the header is shown. Right?
If so, you just need to copy the check like you did when the state is hidden.
if(animating === false){
if(scrollToTop > $('header').height() && scrollstate == 'hidden'){
showSideButtons(true);
// I was added this check `scrollToTop < $('header').height()`
} else if (scrollstate == 'shown' && scrollToTop < $('header').height()){
showSideButtons(false);
}
}
Currently my code snippet hides the navbar as the user scrolls down and brings it back into view as the user scrolls up.
How would I also bring it back into view once the user stops scrolling down (or pauses momentarily)?
Code:
var mywindow = $(window);
var mypos = mywindow.scrollTop();
var up = false;
var newscroll;
mywindow.scroll(function () {
newscroll = mywindow.scrollTop();
if (newscroll > mypos && !up) {
$('.header').stop().slideToggle();
up = !up;
console.log(up);
} else if(newscroll < mypos && up) {
$('.header').stop().slideToggle();
up = !up;
}
mypos = newscroll;
});
Jsfiddle:
http://jsfiddle.net/GM46N/2/
Perhaps you'd want to use a setTimeout at the end of your scroll event and clear it as soon as the event is fired again, as in:
var mywindow = $(window);
var mypos = mywindow.scrollTop();
var up = false;
var newscroll;
mywindow.scroll(function () {
newscroll = mywindow.scrollTop();
clearTimeout(a);
if (newscroll > mypos && !up) {
$('.header').stop().slideToggle();
up = !up;
console.log(up);
} else if(newscroll < mypos && up) {
$('.header').stop().slideToggle();
up = !up;
}
var a = setTimeout(function(){
if (up) {
$('.header').stop().slideToggle();
up = !up;
}
},1400);
mypos = newscroll;
});
I hope this helped you!
You need a timeout.
Since it looks like your using jQuery here is a resource with a bunch of examples for timeout solutions regarding scrolling event: jquery-event-when-user-stops-scrolling
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.
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​