I have some jQuery code that allows for smoothing scrolling on my web pages.
$(document).ready(function(){
// browser window scroll position (in pixels) where the button will appear
var offset = 200,
// duration of the animation (in ms)
scroll_top_duration = 700,
// bind with the button
$back_to_top = $('.back-to-top');
// display and hide the button
$(window).scroll(function(){
( $(this).scrollTop() > offset ) ? $back_to_top.addClass('make-visible-btt') : $back_to_top.removeClass('make-visible-btt');
});
//smooth scroll to top
$back_to_top.on('click', function(event){
event.preventDefault();
$('body,html').animate({
scrollTop: 0 ,
}, scroll_top_duration
);
});
});
$(document).ready(function() {
// browser window scroll position (in pixels) where the button will appear
var offset = 200,
// duration of the animation (in ms)
scroll_top_duration = 700,
// bind with the button
$back_to_top = $('.back-to-top');
// display and hide the button
$(window).scroll(function() {
($(this).scrollTop() > offset) ? $back_to_top.addClass('make-visible-btt'): $back_to_top.removeClass('make-visible-btt');
});
//smooth scroll to top
$back_to_top.on('click', function(event) {
event.preventDefault();
$('body,html').animate({
scrollTop: 0,
}, scroll_top_duration);
});
});
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
display: inline-block;
height: 40px;
width: 40px;
background: url(../images/back-to-top.png) no-repeat;
background-size: contain;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
border: 1px solid #aaa;
visibility: hidden;
opacity: 0;
transition: opacity .3s 0s, visibility 0s .3s;
}
.make-visible-btt {
visibility: visible;
opacity: 1;
transition: opacity 1s 0s, visibility 0s 0s;
}
.section {
border: 1px solid black;
background-color: #ededed;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
jump to last section
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section" id="last"></div>
Back to Top
The code works fine. When users click the "back to top" button on the page, they're automatically smooth scrolled to the top. All good.
The code above, however, doesn't work for in-page links.
text text text
So if a user clicks on a link like the one above, the pages instantly jumps to that section (which is the default behavior).
I added this code to fix that problem:
$(document).ready(function(){
$('a[href*="\\#"]').on('click', function(event){
var href = $(event.target).closest('a').attr('href'),
skip = false;
if (!skip) {
event.preventDefault();
$('html,body').animate({scrollTop:$(this.hash).offset().top}, 500);
}
});
});
$(document).ready(function() {
// browser window scroll position (in pixels) where the button will appear
var offset = 200,
// duration of the animation (in ms)
scroll_top_duration = 700,
// bind with the button
$back_to_top = $('.back-to-top');
// display and hide the button
$(window).scroll(function() {
($(this).scrollTop() > offset) ? $back_to_top.addClass('make-visible-btt'): $back_to_top.removeClass('make-visible-btt');
});
//smooth scroll to top
$back_to_top.on('click', function(event) {
event.preventDefault();
$('body,html').animate({
scrollTop: 0,
}, scroll_top_duration);
});
});
$(document).ready(function(){
$('a[href*="\\#"]').on('click', function(event){
var href = $(event.target).closest('a').attr('href'),
skip = false;
if (!skip) {
event.preventDefault();
$('html,body').animate({scrollTop:$(this.hash).offset().top}, 500);
}
});
});
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
display: inline-block;
height: 40px;
width: 40px;
background: url(../images/back-to-top.png) no-repeat;
background-size: contain;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
border: 1px solid #aaa;
visibility: hidden;
opacity: 0;
transition: opacity .3s 0s, visibility 0s .3s;
}
.make-visible-btt {
visibility: visible;
opacity: 1;
transition: opacity 1s 0s, visibility 0s 0s;
}
.section {
border: 1px solid black;
background-color: #ededed;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
jump to last section
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section" id="last"></div>
Back to Top
So now that's fixed.
But that second code block has created two new problems:
Links with a fragment identifier (e.g., #section-of-page) no longer update in the browser address bar. For example, within a page, on click, the page does scroll smoothly to the target section (so it works), but the web address stays fixed at www.website.com/whatever, when it should update to www.website.com/whatever#section-of-page.
Links with a fragment identifier don't work across pages. In other words, /this-web-page#section-of-page works fine. But /another-web-page#section-of-page and www.another-website.com/whatever#section-of-page both fail (click does nothing).
These problems didn't exist before adding that second code block.
Looking for some guidance on how to fix these problems.
Also if you can suggest a way to integrate all functions into one block of code, that would be great.
Lastly, I know about the CSS scroll-behavior property, but it's still very rudimentary (can't adjust any settings), so I'd rather stick with JS for now.
Thanks.
You can check if the href points to an internal location by creating a URL object from it and checking its host against window.location.host. Then call event.preventDefault and perform smooth scrolling only in that case.
The callback function (third argument) to $.animate can be used to set the hash properly after the scrolling effect.
if (new URL(href, window.location).host === window.location.host) {
event.preventDefault();
$('html,body').animate({
scrollTop: $(this.hash).offset().top
}, 500, function() {
window.location.hash = new URL(href, window.location).hash;
});
}
$(document).ready(function() {
// browser window scroll position (in pixels) where the button will appear
var offset = 200,
// duration of the animation (in ms)
scroll_top_duration = 700,
// bind with the button
$back_to_top = $('.back-to-top');
// display and hide the button
$(window).scroll(function() {
($(this).scrollTop() > offset) ? $back_to_top.addClass('make-visible-btt'): $back_to_top.removeClass('make-visible-btt');
});
//smooth scroll to top
$back_to_top.on('click', function(event) {
event.preventDefault();
$('body,html').animate({
scrollTop: 0,
}, scroll_top_duration);
});
});
$(document).ready(function(){
$('a[href*="\\#"]').on('click', function(event){
var href = $(event.target).closest('a').attr('href');
if (new URL(href, window.location).host === window.location.host) {
event.preventDefault();
$('html,body').animate({
scrollTop: $(this.hash).offset().top
}, 500, function() {
window.location.hash = new URL(href, window.location).hash;
});
}
});
});
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
display: inline-block;
height: 40px;
width: 40px;
background: url(../images/back-to-top.png) no-repeat;
background-size: contain;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
border: 1px solid #aaa;
visibility: hidden;
opacity: 0;
transition: opacity .3s 0s, visibility 0s .3s;
}
.make-visible-btt {
visibility: visible;
opacity: 1;
transition: opacity 1s 0s, visibility 0s 0s;
}
.section {
border: 1px solid black;
background-color: #ededed;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
jump to last section
External link
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section" id="last"></div>
Back to Top
for The 1st problem is because of event.preventDefault();. If you remove this line then browser url will be updated accordingly.
if you see any problems then you try setting the url again after animation completes / finishes.
for syntax refer docs
$('html,body').animate({
scrollTop: $(href).offset().top
}, 500, function () {
window.location.hash = href;
});
For 2nd problem
check wether the callback is hitting on click by putting debug point
Related
I want my navbar to be transparent, but when the user scrolls a bit I want it to change to a solid color and I am using bootstrap for the navbar, I have done the code that is needed with javascript.
I had this javascript in my HTML file, but it doesn't seems to work and I don't really know why
<script>
var myNav = document.getElementById("mynav");
window.onscroll = function() {
use strict";
if (document.body.scrollTop >= 100) {
myNav.classList.add("scroll");
} else {
myNav.classList.remove("scroll");
}
};
</script>
and I have also added the CSS code.
.scroll {
background-color: transparent !important;
transition: all 0.5s ease-in;
}
I don't know why it doesn't work, it is not displaying any errors, I have also manually put the class and it worked so the problem is from the js code and not the CSS.
Use scrollY property of Window object.
See the Snippet below:
var myNav = document.getElementById("mynav");
window.onscroll = function() {
if (window.scrollY >= 100) {
myNav.classList.add("scroll");
} else {
myNav.classList.remove("scroll");
}
};
.scroll {
background-color: transparent !important;
transition: all 0.5s ease-in;
}
.main-container{
height: 1000px;
}
#mynav{
position: fixed;
background-color: gray;
height: 50px;
margin:0 auto;
top: 0;
bottom:0;
line-height: 50px;
padding:5px;
width: 100%;
}
<div class="main-container">
<div class="mynav" id="mynav">
Hello World! this is mynav
</div>
</div>
Try using window.scrollY instead of document.body.scrollTop.
if (window.scrollY >= 100)
You can also use document.documentElement.scrollTop. It's the html element that actually scrolls, not the body. Typically document.body.scrollTop will always be 0.
I am trying to animate images using CSS on a webpage. The animation is working fine but I want to START the animation it only when user reaches at a particular section of the page. Here is my code:
<div class="sec1-right">
<img class="sec1-dmush1" src ="sec1-dmush1.png">
</div>
CSS
.sec1-right{
position: relative;
width: 50%;
float: right;
box-sizing: border-box;
min-height: 600px;
margin-top: 86px;
}
/* first section animation */
.sec1-dmush1 {
animation: fadeAndScale .9s cubic-bezier(.45,-0.22,.34,1.69);
transform-origin:center bottom;
max-width: 150px;
width: 100%;
position: absolute;
left: 180px;
top: 300px;
z-index: 0;
}
Animation
#keyframes fadeAndScale{
from{
opacity:0;
transform: scale3d(0,0,1);
}
to{
opacity: 1;
transform: scale3d(1,1,1);
}
}
How can I achieve
You need to write scroll event in javascript. element offsetTop minus window height. So as soon as element comes in viewport event starts.
Javascript:
var scrollpos = window.scrollY; // window scroll position
var wh = window.innerHeight-50; // as soon as element touches bottom with offset event starts
var element = document.querySelector(".sec1-dmush1"); //element
window.addEventListener('scroll', function(){
if(scrollpos > (element.offsetTop - wh)){
element.classList.add("onScroll");
}
});
JsFiddle
jQuery:
$(window).scroll(function(){
var wh = $(window).height()-50;
if($(window).scrollTop() > $('.sec1-dmush1').offset().top-wh){
$('.sec1-dmush1').addClass('onScroll');
}
});
jsFiddle
If you have multiple elements to animate. You can use waypoint js to reduce some efforts.
var wh = $(window).height();
var waypoints = $('.sec1-dmush1').waypoint(function(direction) {
$(this.element).addClass('onScroll');
console.log(11);
}, {
offset: wh-50
});
jsFiddle
using javascript you can find the scroll event . After the specific px(height). You can add a class to the existing class(jquery) for which you trying to animate.
I know how to make the navigation bar fading into the viewport from the top.
$(document).scroll(function () {
var x = $(window).scrollTop();
console.log(x)
if (x > 699) {
$("header").addClass("fix");
} else {
$("header").removeClass("fix");
}
});
.fix {
position: fixed;
background:#fff;
-webkit-animation: test .5s linear;
}
.fix + main {
padding-top: 100px;
}
#-webkit-keyframes test {
from { top:-100px }
to { top:0 }
}
When it removes the class, it just disappears.
How can i make fade out back to the top?
I log it into console only for testing. (Yes, i wrote the code, it's not copy paste.)
There is no need to use keyframes here, a simple transition will do the trick
You need to put the position fixed and the original top value on the header itself and only update the animated value when scrolling.
The issue you had before was you had position fixed on the fix class, as soon as you remove the fix class, your element is no longer fixed so it will disappear without any animations. In other words, it can only animate if you had the fix class on it.
https://jsfiddle.net/19qdtL3L/
I edited the scroll value so it will show up sooner
$(document).scroll(function() {
var x = $(window).scrollTop();
console.log(x)
if (x > 300) {
$("header").addClass("fix");
} else {
$("header").removeClass("fix");
}
});
header {
top: -100px;
position: fixed;
transition: top 0.5s;
}
.fix {
background: green;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>header element</header>
<br><br><br><br><br>
<br><br><br><br><br>
<br><br><br><br><br>
<br><br><br><br><br> <br><br><br><br><br> <br><br><br><br><br>
<br>sdfdsf
Your actual keyframe is only going one way : down (top: -100px to top: 0). What you want to do can't work like this.
You could do this without a keyframe, only with a transition :
$('button').on('click',function(){
$('div').toggleClass('shown');
});
div{
position: static;
top: -50px;
left: 0;
width: 100%;
height: 50px;
background-color: red;
transition: all .5s;
}
div.shown{
top: 0;
}
button{
margin-top:100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
<button>Click me</button>
I want to get the height of a class that is toggled. When a button is clicked, the class .category-menu-visible is added. If the class exists, then I want to get it's height. But when I alert menuHeight, it is 0.
Small scale JSFiddle example
Actual Code:
jQuery
jQuery('.topics-btn').click(function(){
jQuery('.category-menu-wrap').toggleClass('category-menu-visible');
if (jQuery('.category-menu-wrap').hasClass('category-menu-visible')){
var menuHeight = jQuery('.category-menu-visible').height();
alert(menuHeight);
jQuery('.sidebar .content-wrap').css('margin-top', menuHeight);
} else {
jQuery('.sidebar .content-wrap').css('margin-top', 0);
}
});
CSS:
.category-menu-wrap {
width:100%;
height:0px;
background-color:#F7D5B6;
overflow: hidden;
transition: height .5s cubic-bezier(.27,1.76,.95,1.19);
}
.category-menu-visible {
height: 70px;
transition: height .3s cubic-bezier(.27,1.76,.95,1.1);
}
Why can't it retrieve the height?
You need to wait till transition finishes.
Update:
There is a useful event transitionend to do it:
jQuery('.topics-btn').click(function(){
var $menu = jQuery('.category-menu-wrap');
$menu.toggleClass('category-menu-visible');
$menu.on("transitionend", function(){
if (jQuery('.category-menu-wrap').hasClass('category-menu-visible')){
var menuHeight = jQuery('.category-menu-visible').height();
alert(menuHeight);
jQuery('.sidebar .content-wrap').css('margin-top', menuHeight);
} else {
jQuery('.sidebar .content-wrap').css('margin-top', 0);
}
});
});
.category-menu-wrap {
height: 0;
}
.category-menu-visible {
height: 70px;
transition: height .3s cubic-bezier(.27,1.76,.95,1.1);
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<button class='topics-btn'>Click</button>
<div class='category-menu-wrap'></div>
The problem you are having is the CSS transition. When you click the height is calculated but at that moment it is 0. After the transition it will have the 70px value. You need to get the height after the transition finishes.
In this example the transition duration is set to 0s.
jQuery('.topics-btn').click(function(){
jQuery('.category-menu-wrap').toggleClass('category-menu-visible');
if (jQuery('.category-menu-wrap').hasClass('category-menu-visible')){
var menuHeight = jQuery('.category-menu-visible').height();
alert(menuHeight);
jQuery('.sidebar .content-wrap').css('margin-top', menuHeight);
} else {
jQuery('.sidebar .content-wrap').css('margin-top', 0);
}
});
.category-menu-wrap {
width:100%;
height:0px;
background-color:#F7D5B6;
overflow: hidden;
transition: height 0s cubic-bezier(.27,1.76,.95,1.19);
}
.category-menu-visible {
height: 70px;
transition: height 0s cubic-bezier(.27,1.76,.95,1.1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="topics-btn">.topics-btn</button>
<div class="category-menu-wrap"></div>
In this other example we rely on the transitionend event to get the height value:
jQuery('.topics-btn').click(function(){
jQuery('.category-menu-wrap').toggleClass('category-menu-visible');
});
jQuery('.category-menu-wrap').on('transitionend',function(){
if (jQuery('.category-menu-wrap').hasClass('category-menu-visible')){
var menuHeight = jQuery('.category-menu-visible').height();
alert(menuHeight);
jQuery('.sidebar .content-wrap').css('margin-top', menuHeight);
} else {
jQuery('.sidebar .content-wrap').css('margin-top', 0);
}
});
.category-menu-wrap {
width:100%;
height:0px;
background-color:#F7D5B6;
overflow: hidden;
transition: height 0.5s cubic-bezier(.27,1.76,.95,1.19);
}
.category-menu-visible {
height: 70px;
transition: height 0.5s cubic-bezier(.27,1.76,.95,1.1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="topics-btn">.topics-btn</button>
<div class="category-menu-wrap"></div>
From the official docs
The value reported by .height() is not guaranteed to be accurate when the element or its parent is hidden. To get an accurate value, ensure the element is visible before using .height()
The problem here is exactly this: your .category-menu-visible is not visible. when JQuery looks for it. This is due to the transition property set with a duration on the toggled class.
Update (according to JSFiddle)
It appears that when toggling a class, the height isn't recognized on the toggled class unless a unit of measurement is specified.
This occurs even without the transition property.
Not working example - height: 70;
Working Example - height: 70px;
I'm using Stick-Kit to keep some images in place while scrolling, and it seems to be affecting another script that initiates a CSS animation by adding a class to a div when it enters the viewport. I assume the Sticky-Kit script is 'reseting' the other, as the animation only occurs once when Sticky-Kit is removed. The issue is visible when the animated div gets to the top of the screen. How do I ensure the animation occurs only one time (when it first appears in the viewport)?
http://codepen.io/SeanLindsay1/pen/ZBVyLZ
HTML
<div id="bg">
<h2 class="header-title"><span>HEADER</span></h2>
<div id="pic1">
1
</div>
<div id="pic2">
2
</div>
<div id="pic3">
3
</div>
</div>
CSS
/* STICKY-KIT */
#bg {
background-color: white;
width:100%;
height:1500px;
padding:0;
margin:0;
font-size:30px
}
#pic1 {
position:relative;
width:60% ;
height:500px;
background-color:blue;
}
#pic2 {
position:relative;
width:60% ;
height:500px;
background-color:green;
}
#pic3 {
position:relative;
width:60% ;
height:500px;
background-color:red;
}
/* HEADER TITLES */
.header-title span {
display: inline-block;
position: relative;
}
.change:after {
content: " ";
position: absolute;
height: 5px;
border-bottom: 1px solid #bebebe;
-webkit-animation: extend .1s 1 forwards;
animation: extend 1s 1 forwards;
margin-left: 4px;
top: 1.2em !important;
}
#-webkit-keyframes extend {
0% {
width: 0;
}
100% {
width: 200px;
}
}
#keyframes extend {
0% {
width: 0;
}
100% {
width: 200px;
}
}
jQuery
// Check to see if element is in viewport
function isElementInViewport(elem) {
var $elem = jQuery(elem);
// Get the scroll position of the page.
var scrollElem = ((navigator.userAgent.toLowerCase().indexOf('webkit') != -1) ? 'body' : 'html');
var viewportTop = jQuery(scrollElem).scrollTop();
var viewportBottom = viewportTop + jQuery(window).height();
// Get the position of the element on the page.
var elemTop = Math.round( $elem.offset().top ) + 200 ;
var elemBottom = elemTop + $elem.height();
return ((elemTop < viewportBottom) && (elemBottom > viewportTop));
}
// Check if it's time to start the animation
function extendLine() {
var $elem = jQuery('.header-title span').each(function() {
var $elem = jQuery(this);
// If the animation has already been started
if ($elem.hasClass('change')) return;
if (isElementInViewport($elem)) {
// Start the animation
$elem.addClass('change');
}
});
}
// Capture scroll events
jQuery(window).scroll(function(){
extendLine();
});
$("#bg").stick_in_parent();
$("#text").stick_in_parent({offset_top: 390});
$("#pic1").stick_in_parent();
$("#pic2").stick_in_parent();
$("#pic3").stick_in_parent();
If possible, you can use a CSS Transition instead of an Animation. It'll have better browser support, and will work. I can't really find out what's happening in your code, but if you change a couple of lines, it'll work as expected.
Here is a forked codepen: http://codepen.io/ddanielbee/pen/BQbQqj
Here are the specific lines:
.header-title span::after {
content: " ";
transition: all 1.5s ease-out;
width: 0;
}
.header-title span.change::after {
position: absolute;
height: 5px;
border-bottom: 1px solid #bebebe;
width: 200px;
margin-left: 4px;
top: 1.2em !important;
}
Removing this line of code:
$("#bg").stick_in_parent();
ensures that the header text block is not influenced by Stick-Kit and eliminates the problem of repeated execution of the animation, as shown in this codepen.
I haven't observed any ill effects caused by that change, but I cannot guarantee that there aren't any, since I don't know why this line was in the original code.