I have 2 div with height and width of 100%. one is top:0px and the other is top 100%;
I want to go from one to the other one with an animation which start when I use the mousewheel. it worked fine with my code.
$(window).bind('mousewheel DOMMouseScroll', function(event){
if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
TweenLite.to(window, 2, {scrollTo:{y:$( "#one").offset().top}});
} else {
TweenLite.to(window, 2, {scrollTo:{y:$("#two").offset().top}});
}
}
But I want to stop that script when I enter in the second div and then use the wheel as usual for the rest of the page. ( other full pages div)
So I can do
....
else {
TweenLite.to(window, 2, {scrollTo:{y:$("#two").offset().top}});
$(window).unbind(); (but i dodn't know if it's really ok )
}
That works fine. But now I would like to make the wheel script work again when we reach the top of the div "two". I tried with conditions but i couldn't make it work.
This is my code (you can also see it on this codepen where it works):
$(window).bind('mousewheel DOMMouseScroll', function(event){
if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
TweenLite.to(window, 2, {scrollTo:{y:$( "#one").offset().top}, ease:Expo.easeOut});
}
else {
TweenLite.to(window, 2, {scrollTo:{y:$( "#two").offset().top}, ease:Expo.easeOut});
}
});
body{overflow:hidden}
#one {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 1;
background:#733;
}
#two {
position: absolute;
width: 100%;
height: 90%;
left: 0;
top: 100%;
z-index: 1;
background:#439;
}
#three {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 190%;
z-index: 1;
background:#896;
}
<div id="one"></div>
<div id="two"></div>
<div id="three"></div>
This solution is not pretty, but it works. You could use it as a workaround while somebody (or yourself or myself) look for a better solution.
var animation = false;
var scrollby = 20;
$(window).bind('mousewheel DOMMouseScroll', function(event){
if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
if ($( "#two").offset().top == $(window).scrollTop()) {
TweenLite.to(window, 2, {scrollTo:{y:$( "#one").offset().top}, ease:Expo.easeOut, onComplete:function() { animation=false; }, onStart: function(){ animation=true; } });
} else {
if (!animation){
if ($(window).scrollTop() - scrollby < $( "#two").offset().top) {
$(window).scrollTop($( "#two").offset().top);
} else {
window.scrollBy(0,-scrollby);
}
}
}
}
else {
if ($(window).scrollTop() == 0) {
TweenLite.to(window, 2, {scrollTo:{y:$( "#two").offset().top}, ease:Expo.easeOut, onComplete:function() { animation=false; }, onStart: function(){ animation=true; }});
} else {
if (!animation) {window.scrollBy(0,scrollby);}
}
}
});
You can see it working on this codepen.
Your initial code did this:
If the mousewheel goes down, scroll down to #two.
If the mousewheel goes up, scroll up to #one.
The modified code above changes a bit:
If the mousewheel goes down:
If you are at the top of the page (#one), scroll down to #two.
Or else (you are in #two or below), scroll down a given amount.
If the mousewheel goes up:
If you are at #two, then scroll up to #one.
If you are not at #two (you are below that point), scroll up a given amount.
I also added a couple of checks to avoid issues:
Check if there is an animation (TweenLite.to) going on before scrolling. This is achiveved by setting the animation variable on the onStart and onComplete events of TweenLite.
If scrolling up you go above #two, autocorrect and go to #two. This issue only happens when you scroll to the bottom of the page and then scroll up.
Related
I want to make two functions, "functionattoppage" and "functionat1000pxpage". I want to make the function "functionattoppage" activate when the user is at the top of a webpage and "functionat1000pxpage" activate when the user is down about 1000px from the top of the page, is this possible?
Here is the only thing I could come up with:
window.onscroll = function () {
scrollFunction()
};
function scrollFunction() {
if (document.body.scrollTop > 1000 || document.documentElement.scrollTop > 1000) {
document.body.classList.add("body.changed");
}
if (document.body.scrollTop > 0 || document.documentElement.scrollTop > 0) {
document.body.classList.remove("body.changed");
}
}
<h1>Please look at the JavaScript section, it is what my best guess would be on how to make this work</h1>
Sorry if I didn't explain this very well, I don't have much time right now. If you need more information please ask me.
After console logging document.body.scrollTopin the fiddle came out to be zero. Plus your conditions are bound to fail because when scrollTop > 1000 then it is > 0 too.
you should either make it a else or move the >0 condition to the first
window.onscroll = function() {
scrollFunction()
};
function scrollFunction() {
console.log(document.documentElement.scrollTop);
if (document.documentElement.scrollTop > 200) {
document.getElementsByTagName('h1')[1].classList.add("changed");
}
else{
document.getElementsByTagName('h1')[1].classList.remove("changed");
}
}
.changed {
color: red
}
<h1>Please look at the JavaScript section, it is what my best guess would be on how to make this work</h1>
<div style="height: 400px;background: grey;"></div>
<h1>Please look at the JavaScript section, it is what my best guess would be on how to make this work</h1>
<div style="height: 400px;background: grey;"></div>
Looks like you are trying to hide a class when the scroll position for the window is within 0 and 1000 pixels yes? The a conditional that will check if the scrollY position of the window, window.scrollY is greater than 0, but less than 1000, add the class else remove the class.
window.onscroll = () => {
scrollFunction()
}
const scrollFunction = () => {
window.scrollY > 0 && window.scrollY < 1000 ?
document.body.classList.add("changed") :
document.body.classList.remove("changed")
document.getElementById("scroll_position").textContent = window.scrollY;
}
#scroll_position {
position: fixed;
top: 0;
right: 0;
background: red;
color: #fff;
padding: 8px;
width: 100px;
}
#cont {
height: 3000px;
}
#cont h1 {
position: sticky;
top: 50px;
}
.changed {
background: black;
color: white;
}
<p id="scroll_position"></p>
<div id="cont">
<h1>Please look at the JavaScript section, it is what my best guess would be on how to make this work</h1>
</div>
You can use a div as your marker. And when it is >= 0 execute one function and when it gets to -1000px execute another. Use getBoundingClientRect() to get an elements coordinates.
HTML
<div id=“marker1”></div>
JS
window.addEventListener( "scroll", function(){
let marker1 = document.getElementById("marker1").getBoundingClientRect();
if (marker1.top >= 0) atTop();
if (marker1.top <= -1000) at1000px();
})
function atTop() {
document.body.style.backgroundColor = "blue";
}
function at1000px() {
document.body.style.backgroundColor = "red";
}
Be aware the atTop won’t execute until you start scrolling because of the type of event listener. If you want it executed on load also then you’ll need to add that.
You can do it through sensing the scroll event as you are doing, but it's quite overheady.
Instead you could use an IntersectionObserver on a couple of elements, This will tell you when they come into and go out of the viewport and you don't have to worry about any intermediate scrolling.
If you already have elements that you want to sense in those positions you could sense those going into and out of the viewport. If not you can 'plant' a couple of 1px divs at the top and at 1000px and sense them coming in and out.
This snippet just logs the comings and goings to the console but of course you put whatever code you want in their place.
function callback (entries) {
for (let i = 0; i < entries.length; i++) {
if (entries[i].isIntersecting) {console.log(entries[i].target.classList + ' is in the viewport'); }
else {console.log(entries[i].target.classList + ' is not in the viewport'); }
}
}
const observer = new IntersectionObserver(callback);
const sensors = document.querySelectorAll('.sensor');
for (let i = 0; i < sensors.length; i++) {
observer.observe(sensors[i]);
}
.talldiv {
width: 100vw;
height: 2000px;
background-image: linear-gradient(to bottom, red, blue, orange, green, purple, cyan); /*just so we notice scrolling */
}
.sensor {
position: absolute;
left: 50%;
top: var(--top);
width: 1px;
height: 1px;
background: transparent;
}
.sensetop {
--top: 0px;
}
.sense1000px {
--top: 999px;
}
<div class="sensor sensetop"></div>
<div class="sensor sense1000px"></div>
<div class="talldiv"></div>
I'm trying to make a header that appears at a certain place of the page.
So what I'm doing is checking the scroll to top of the page and the top offset of the element after which the header should appear. If the scrollTop is greater than offset the header is shown, otherwise it disappears.
But! When I scroll to the place, the header position is constantly switching between top: -13% and top: -12.999998%. After some time it finally shows the header but it never disappears.
What am I doing wrong?!
JSFiddle: https://jsfiddle.net/5k5s016f/
Well, i think the problem is that the .animate() functions are running constantly, causing the animations to "restart" before its ends.
It is not the most beautiful solution, but just adding a flag that controls the execution of the functions and a timeout to run the handler less frequently solves the problem.
https://jsfiddle.net/5k5s016f/2/
var visible = false;
$(window).scroll(function() {
setTimeout(function(){
var height = $(window).scrollTop();
var $page2 = $("#page2");
var offset = $page2.offset().top;
if (height > offset) {
if (visible) {
return;
}
visible = true;
$(".floating-header").show().animate({
top: 0
});
} else {
if (!visible) {
return;
}
visible = false;
$(".floating-header").animate({
top: "-13%"
});
}
}, 200)
});
The issue you are seeing is because each time a scroll event gets called animation queues up. If you wait long enough, you can see that the animation to set top to 0 actually works.
You can use the stop() function to stop all animation before attempting to run another one.
Something like this
if (height > offset) {
$(".floating-header").stop().show().animate({
top: "0"
}, 700);
} else {
$(".floating-header").stop().animate({
top: "-13%"
}, 700);
}
A couple of improvements I can suggest are
Debounce the scroll event handler
Check the current state of the header before queuing animation. i.e. do not try to hide it if it is already hidden and vice versa
Your logic is all messed up. Basically, you want to make sure that you are only animating when you absolutely need to - no more, no less. And since scroll events happen hundreds of times... constantly rapid firing as the user scrolls... you want to make sure you are doing the least amount of work possible during each scroll event. This especially means that you don't want to be querying the DOM on every scroll event if you don't have to (ps. $('selector') is a dom query). Take a look at this fiddle:
https://jsfiddle.net/5k5s016f/6/
Looks like I'm last to the party due to interruptions, but since I wrote it up I'll post the answer FWIW.
jsFiddle Demo
You need to debounce your code. Here is a simple system, but studing Ben Alman's explanation/examples is also recommended.
var $m1 = $('#m1'), $m2 = $('#m2'); //TESTING ONLY
var $win = $(window), $page2 = $("#page2"), $hdr=$(".floating-header");
var $offset = $page2.offset().top;
var hvis = false, curpos;
$win.scroll(function() {
curpos = $win.scrollTop();
$m1.html(curpos); //TESTING ONLY
$m2.html($offset);//TESTING ONLY
if ( curpos > $offset ) {
if ( !hvis ){
hvis = true;
//$m1.html(curpos);
$hdr.finish().animate({
top: "0"
}, 700);
}
} else {
if ( hvis ){
$hdr.finish().animate({
top: "-60px"
}, 700);
hvis = false;
}
}
});
html,
body {
height: 100vh;
width: 100vw;
}
#page1,
#page2,
#page3 {
height: 100%;
width: 100%;
background-color: #fff;
}
.floating-header {
position: fixed;
top: -60px;
background-color: #000;
width: 100%;
height: 60px;
}
.msg{position:fixed;bottom:10px;height:30px;width:80px;text-align:center;}
.msg{padding-top:10px;}
#m1 {left:3px; border:1px solid orange;background:wheat;}
#m2 {right:3px;border:1px solid green; background:palegreen;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<header class="floating-header">Header</header>
<div id="page1">
<p>Page1</p>
</div>
<div id="page2">
<p>Page2</p>
</div>
<div id="page3">
<p>Page3</p>
</div>
<div id="m1" class="msg"></div>
<div id="m2" class="msg"></div>
Im trying to make '.pierre' .moveDown once the image has a left position of 20% but its not working. Whats supposed to happen is once the image has a left position of 20% it needs to fall down the page by itself. The html and css code is located in the fiddle.
Heres a link to a fiddle
https://jsfiddle.net/Pallonej/0vhc7pqq/
this is my script thats not working
if ($('.pierre').css('left') == '20%') {
$(this).moveDown();
}
Your check for the position should be inside of the keydown handler and then parse the value from $.css(..) to an integer to have your comparison work.
The code could look like in the updated fiddle here.
I've also added stop to you animation to avoid animation queuing. Maybe it's better to do this with-out animation.
With $(document).width() * 0.2 you can calculate the relative position for the falling position.
If you want to hide or remove the box once it is fallen you can add a done callback to the animation to do what you're looking for. In the demo I've hidden the box.
//move person
$(document).keydown(function (e) {
if (e.keyCode == 37) { // left
$(".pierre").stop(true,true).animate({
left: "-=30"
});
} else if (e.keyCode == 37) { // right
$(".pierre").stop(true,true).animate({
left: "-=30"
});
}
//console.log($('.pierre').css('left'));
if (parseInt($('.pierre').css('left')) <= $(document).width() * 0.2 ) {
//$(this).slideDown();
$('.pierre').animate({
top: "+=" + $(document).height()
}, function() {
$(this).hide();
});
}
});
//fall
/*if ($('.pierre').css('left') === '+5px') {
//$(this).slideDown();
$(this).animate({
left: "-=30"
});
}*/
.pierre {
height:15%;
width:15%;
background: black;
position: absolute;
left: 90%;
top: -.7%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='pierre'></div>
You need to work with the offset property and not position
Here's an example for your question with extra feature to illustrate the concept :
var position = $(".pierre").offset();
$(document).keydown(function(e) {
var y= $(".pierre").offset().top;
var x = $(".pierre").offset().left;
if (e.keyCode == 37) { // left
$("#position").html("X: "+x+" , Y:"+y);
$(".pierre").animate({
left: "-=30"
});
} else if (e.keyCode == 37) { // right
$(".pierre").animate({
left: "+=30"
});
}
if (x<=200) {
$(".pierre").animate({
top: "+=30"
});
}
});
and here is the JSFiddle
As Alundra alreay mentioned, you should use .offset().
You can add a callback to the .animate function, which will be executed after the animation has finished. If the location is right, you can call stop, in order to stop all current (and future) animation, so it won't move left again for the times you button-bashed left. Then you can again animate it to let it fall.
The new js code (I removed the second right-left button part for readability):
//move person
$(document).keydown(function(e) {
if (e.keyCode == 37) { // left
$(".pierre").animate({
left: "-=30"
}, function(){
if($('.pierre').position().left < window.innerWidth/5){
$('.pierre').stop(true);
$('.pierre').animate({
top: "+=1000"
});
}
});
}
});
I have a header which position:absolute on load I need to display it fix on particular scrolling so it working ..
but problem is that how I use header effect (i.e display with delay from upward) with position:fixed property.
code:
CSS
.iaw-header {
position:absoulte
}
JS:
{
if (jQuery(window).scrollTop() >= 700) {
jQuery('.iaw-header').css('position','fixed');
});
}
HTML
<div id="header">
Header text here.
</div>
CSS
.header { position: absolute; }
JS
if (jQuery(window).scrollTop() >= 700) {
$('#header').css('top', '-300px');
$('#header').css('position', 'fixed');
$('#header').animate({top: 0}, 1000);
} else {
$('#header').animate({top: '-300px'}, 1000, function () {
$('#header').css('top', 0);
$('#header').css('position', 'absolute');
});
}
So when the site loads (in CSS), the header can have top: -300px;, and when the user scrolls, you transition (or set) the header's top to 0px, so it scrolls down from the top.
$(window).scroll(function () {
var i = $('.iaw-header')
var h = i.outerHeight(true);
if ($(window).scrollTop() > h) {
if (!i.hasClass('fixed')) i.hide().addClass('fixed');
}
if ($(window).scrollTop() >= 400) {
i.slideDown('fast');
} else {
i.removeClass('fixed').show();
}
});
Add a class in your style:
.fixed {position: fixed;top:0; left:0;width: 100%; }
Perhaps, not the best code but still you can start building on it and modify to make it better. Here is the Jsfiddle link :http://jsfiddle.net/lotusgodkk/gxRC9/200/
As my title says, my sticky header is causing the subsequent div to jump about 100 pixels when the navbar reaches the top of the page. It's like the home div is magically losing 100 pixels of its height. I've tried a couple things but haven't been able to get this to work.
I have added plugins for smooth scrolling but couldn't get it to work in the jsfiddle. If you scroll down slowly when the navbar is getting to the top of the page, you will notice the skip.
Thanks for your help!
http://jsfiddle.net/g9N78/2/
here is the code I'm using for the sticky header:
<script>
function moveScroller() {
var move = function() {
var st = $(window).scrollTop();
var ot = $("#nav").offset().top;
var s = $(".nav");
if(st > ot) {
s.css({
position: "fixed",
top: "0",
background: "rgba(0,0,0,0.65)"
});
} else {
if(st <= ot) {
s.css({
position: "",
top: "",
background: "black"
});
}
}
};
$(window).scroll(move);
move();
}
</script>
<script type="text/javascript">
$(function() {
moveScroller();
});
</script>
Since you are removing that object from the DOM flow the space is available and the element under takes it, you can just add some margin to #home like this:
$('#home').css('marginTop','100px');
Check this Demo http://jsfiddle.net/g9N78/3/
Use the jQuery .height() method to find out the height of your nav bar, save it to a variable, then apply that height to the top margin of the page, to make it fill the space that the nav bar used to occupy.
$(".nav").height();
$('#home').css('marginTop', navHeight);
See the fiddle below...
http://jsfiddle.net/g9N78/8/
jQuery:
function moveScroller() {
var move = function() {
var st = $(window).scrollTop();
var ot = $("#nav").offset().top;
var s = $(".nav"),
navHeight = s.height();
if(st > ot) {
s.css({
position: "fixed",
top: "0",
background: "rgba(0,0,0,0.65)"
});
$('#home').css('marginTop', navHeight);
} else {
if(st <= ot) {
s.css({
position: "",
top: "",
background: "black"
});
}
$('#home').css('marginTop', '0');
}
};
$(window).scroll(move);
move();
}
There is no magic involved. As long as you do not reach the scroll-top, the header is position:static. The following div will be displayed under the Top. As soon as the element is set to position: fixed, you "lose" the height of the header, causig the optical jump.
I don't think you need Javascript for your header to be sticky.
Try removing the javascript and add this css:
body
{
padding-top: 90px;
}
.nav
{
position: fixed;
top: 0px;
height: 90px;
width: 100%;
}
Edit: Sorry I did not think about your Logo. So this will not work for you like that.