jQuery scrollTop - Issue with wrong hash - javascript

I am trying to do scrollTop animation to an anchor that resides inside of a fullscreen <section>, but does not scroll to right anchor at first click. Here's my code.
<nav id="scroller"> Scroll me to sub 1
Scroll me to sub 2
Scroll me to sub 3
</nav>
<section id="boxTop"></section>
<section id="boxMaster">
<section id="subBox1">
<p>Hello. I am the Sub 1!</p>
</section>
<section id="subBox2">
<p>Hello. I am the Sub 2!</p>
</section>
<section id="subBox3">
<p>Hello. I am the Sub 3!</p>
</section>
</section>
$("#scroller a").click(function () {
$('#boxMaster').animate({
scrollTop: $(this.hash).offset().top
}, 700);
$("#scroller a").removeClass("active");
$(this).addClass("active");
});
fiddle
$("#scroller a").click(function() {
$('#boxMaster').animate({
scrollTop: $(this.hash).offset().top
}, 700);
$("#scroller a").removeClass("active");
$(this).addClass("active");
});
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#scroller {
position: fixed;
top: 0;
height: 30px;
text-align: center;
}
#scroller a {
color: #fff;
margin: 0 20px;
text-decoration: none;
}
#scroller a.active {
font-weight: bold;
text-decoration: underline;
}
#boxTop {
width: 100%;
height: 100%;
background: red;
}
#boxMaster {
width: 100%;
height: 100%;
background: blue;
overflow: hidden;
}
#boxMaster #subBox1,
#boxMaster #subBox2,
#boxMaster #subBox3 {
width: 100%;
height: 100%;
display: table;
}
p {
color: #fff;
text-align: center;
display: table-cell;
vertical-align: middle;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav id="scroller"> Scroll me to sub 1
Scroll me to sub 2
Scroll me to sub 3
</nav>
<section id="boxTop"></section>
<section id="boxMaster">
<section id="subBox1">
<p>Hello. I am the Sub 1!</p>
</section>
<section id="subBox2">
<p>Hello. I am the Sub 2!</p>
</section>
<section id="subBox3">
<p>Hello. I am the Sub 3!</p>
</section>
</section>

Add the current scroller value to the offset().top(), the latter beeing relative to the top of frame, and get rid of this.hash. Use this.href instead.
$("#scroller a").click(function () {
var y=$('#boxMaster').scrollTop()
$('#boxMaster').animate({
scrollTop: $(this.href).offset().top + y
}, 100);
});

You need to scroll the #boxMaster element relative to the link's position within the element and relative to the #boxMaster element's top position within the body element.
You can do this by adding the #boxMaster element's scrollTop() value with its top position, and then subtracting that from the link's offset top value:
$(this.hash).offset().top - $('#boxMaster').position().top + $('#boxMaster').scrollTop()
Updated Example
var $boxMaster = $('#boxMaster');
$boxMaster.animate({
scrollTop: $(this.hash).offset().top - $boxMaster.position().top + $boxMaster.scrollTop()
}, 700);
You may also need to prevent the link element's default behavior using e.preventDefault(), and then manually scroll the html/body element to the #boxMaster element:
Updated Example
$('html, body').animate({
scrollTop: $boxMaster.offset().top
}, 700);

I have a fixed header. I started with w3school's code. However, I've been struggling with this same problem for so long, finally found a workaround for the "first click incorrect" issue:
Just before (outside) of my click event, I simply created a variable "x", initialized:
var x=1;
Then I have a conditional statement inside the click event checking for x:
if (x==1) {
console.log("x is now: " + x);
x=0;
console.log("x is now: " + x);
jQuery("html, body").animate({
scrollTop: jQuery("div.class-of-element-i-am-scrolling-to" + hash).position().top - jQuery("div.header-container").outerHeight(true) - jQuery("h3.another-element-in-my-way").outerHeight(true)
}, 2000, function(){
return false;
});
} else {
jQuery("html, body").animate({
scrollTop: jQuery("div.class-of-element-i-am-scrolling-to" + hash).position().top
}, 2000, function(){
return false;
});
}
In other words, using "x" as a flag to check if it is the first time the code is run.
If it is, then I am kind of cheating by subtracting the fixed header and the other elements that are pulling my desired div up. Remember to make "x=0" to "drop" the flag.
If it isn't, then it works fine anyway.

Related

My Sticky Header Cover Content with the Smooth Scrolling Effect

I added a sticky header and a smooth scrolling effect, and I cannot figure out how to fix the position so it counts with the header size. The things I have tried disable the sticky header completely.
I have tried to use several different techniques, although I am a newbie and it might be too hard for me to do by myself.
<div id="container">
<section id="sectionHome">
<!--Header and Logo-->
<header id="myHeader">
<logo>
<img src="Pictures/Marvel-logo-880x660.crop.png">
</logo>
</header>
<!--The Top Navigation Menu-->
<div id="mainNav">
<ul>
<li class="current">Home</li>
<li>Characters</li>
<li>Movies</li>
<li>More Info</li>
</ul>
</div>
</section>
//Smooth Scrolling in Main Nav
$(document).ready(function() {
$('#mainNav li a').click(function(e) {
var targetHref = $(this).attr('href');
$('html, body').animate({
scrollTop: $(targetHref).offset().top
}, 1000);
e.preventDefault();
});
});
// Sticky Header
window.onscroll = function() {
myFunction()
}; // When the user scrolls the page
var header = document.getElementById("sectionHome"); // Get the header and top nav
var sticky = header.offsetTop; // Get the offset position of the navbar
function myFunction() { // Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position
if (window.pageYOffset > sticky) {
header.classList.add("sticky");
} else {
header.classList.remove("sticky");
}
}
This was one thing I tried, but it disabled my sticky header:
$(document).ready(function() {
var headerHeight = $('header').outerHeight(); // Target your header navigation here
$('#main-nav li a').click(function(e) {
var targetHref = $(this).attr('href');
$('html, body').animate({
scrollTop: $(targetHref).offset().top - headerHeight // Add it to the calculation here
}, 1000);
e.preventDefault();
});
});
I thought I could set a value for the total header size and position it that way, although it disables the sticky header. How do I do this properly?
This is my webpage:
http://www.student.city.ac.uk/~aczc972
Best regards,
Danielle
I have added a sandbox how to do it using jQuery, generally speaking only one addition from my site is that I am checking what is the target e.g. scroll to top page, and if yes, I am running specified code for it:
if (targetHref === "#") {
$("html, body").animate(
{ scrollTop: 0 },
"1000"
);
} else {
$("html, body").animate({scrollTop: $(targetHref).offset().top},1000);
}
codesandbox.io/s/81l87w26w0
Subtract header height scroll to prevent covering content by header
scrollTop: $(targetHref).offset().top - 180"
You can also scroll to top of the page like:
Add id="home" to body and change href in:
<li class="current">Home</li>
to home i.e.
<li class="current">Home</li>
Should work with your code
This is not necessarily the best way to do this, but it's an example which is designed to illustrate how it can be done. You don't need jQuery to achieve this effect so it's worth trying it without.
The code below fixes the header, and adjusts the padding of the main wrapper to account for the size of the header. It then sets up listeners on elements with the class section-link. For those elements, the click event will scroll to the element with the id which corresponds to the data-section attribute for the element which was clicked.
You can ignore the css for this which was only added to illustrate how this might work.
const padForHeader = () => {
// find out how high the header element is
const headerHeight = document.getElementById('top-header').clientHeight;
// how much extra padding would we like?
const headerPadding = 20;
// add the two together to see how much padding we need to add
const headerBufferSize = headerHeight + headerPadding;
// set the marginTop property so that the header doesn't overlay content
document.querySelector('.wrapper').style.marginTop = `${headerBufferSize}px`;
};
padForHeader();
// when the window resizes, re-pad for the header
window.addEventListener('resize', padForHeader);
document
.querySelectorAll('.section-link')
.forEach(element => {
// we want to scroll 'smoothly' to the element
const scrollOptions = {
behavior: "smooth"
};
// we can read the data attribute to find the matching element's id
const elementIdToScrollTo = element.dataset.section;
// we can use the id we found to get the corresponding element
const elementToScrollTo = document.getElementById(elementIdToScrollTo);
// we can set the onclick property to scroll to the element we found
element.onclick = () => elementToScrollTo.scrollIntoView(scrollOptions);
});
.header {
background-color: red;
border: solid 2px grey;
border-radius: 5px;
font-family: arial;
margin: 0 auto;
position: fixed;
top: 0;
left: 0;
right: 0;
width: 97%;
}
.header>ul {
list-style: none;
color: rgba(250, 250, 240, 0.8);
}
.header>ul>li {
cursor: pointer;
display: inline-block;
position: relative;
top: 0px;
transition: all 0.3s ease;
width: 200px;
}
.header>ul>li:hover {
color: rgba(250, 250, 240, 1);
top: -1px;
}
.section {
background-color: rgba(20, 20, 30, 0.2);
height: 80vh;
border-bottom: solid 2px black;
padding-top: 50px;
}
<div class="wrapper">
<div id="top-header" class="header sticky">
<ul>
<li class="section-link" data-section="1">Item 1</li>
<li class="section-link" data-section="2">Item 2</li>
<li class="section-link" data-section="hello-world">Item hello world</li>
</ul>
</div>
<div id="1" class="section">
Test 1
</div>
<div id="2" class="section">
Test 2
</div>
<div id="hello-world" class="section">
Test 3
</div>
</div>

one single scroll button that scrolls to multiple elements in jquery

I have the next problem: let's say that I have 6 divs with height 700px and one single fixed button.
< a class="button >< /a>
< div class="div1" >< /div >
...
< div class="div6 " > < /div >.
and every time the user clicks that single fixed button I want to be scrolled down to the next div and so on. Like a carousel (but on vertical).
E.g. the user only sees div1 then he presses the button and it's automatically scrolled to div2. Then he clicks the button again and it's scrolled down to div3, and so on with a little smooth effect.
Here is what I did, but it only works one time:
$(".button").click(function() {
$('html,body').animate({
scrollTop: $(".div1").offset().top},
'slow');
});
I know how to to this but with 6 different buttons and yeah, that's not the best idea.
Try this out
JS
var count = 0,
sections = $('.section'),
scrollTo = function(index){
$('html, body').animate({
scrollTop: sections.eq(index).offset().top
}, 'slow');
};
$(".next").click(function(){
count++;
scrollTo(count);
});
$(".prev").click(function(){
count--;
scrollTo(count);
});
CSS
.navigation {
position: fixed;
right: 20px;
top: 20px;
}
.button {
display: block;
margin-bottom: 10px;
}
.section {
border-bottom: 2px solid #000;
font-size: 100px;
height: 700px;
line-height: 700px;
text-align: center;
}
HTML
<div class="navigation">
<button class="next">Next</button>
<button class="prev">Prev</button>
</div>
<div class="sections">
<div class="section">1</div>
<div class="section">2</div>
<div class="section">3</div>
<div class="section">4</div>
<div class="section">5</div>
</div>
DEMO

JQuery Offset issue when div position is fixed

I have a website which has a page layout and style something like mentioned in this JsFiddle
Now Using JQuery when I click on the button, content is being displayed properly as shown below:
But when I first scroll the page and then click the button, content is not displaying properly as shown:
Can you please guide me to handle this situation ?
I have used below jQuery for this. But it seems offset or position is not working
$('#btn').click(function(){
var t = $(this).offset();
console.log(t);
$('.control-container').css('top', t.top + 20 + 'px');
$('.control-container').css('display', 'block');
});
$(document).on('scroll', function(){
$('.control-container').css('display', 'none');
});
You don't need to use offset to achieve that... And if you need to keep CSS with position:fixed, you need to switch it in javascript to static.
The thing you are looking for is simply display:table ...
$('#btn').click(function(){
$('.control-container').css({'display': 'table','position': 'static'});
});
$(document).on('scroll', function(){
$('.control-container').css({'display': 'none','position': 'fixed'});
});
Check out this JSFiddle
But if you really need a solution with position:fixed based on button position, you should try this way:
$('#btn').click(function(){
var button_fixed_position = $('#btn').get(0).getBoundingClientRect();
$('.control-container').css({'display': 'block','left' : button_fixed_position.left, 'top' : button_fixed_position.bottom});
});
$(document).on('scroll', function(){
$('.control-container').css({'display': 'none'});
});
Check out second JSFiddle
There is no need to specifically mention position property here.
Also remove the closing a tag and replace it with </button>
Currently container is occupying full width ,but that can also be set
$('#btn').click(function() {
var t = $(this).offset();
console.log(t);
$('.control-container').css('top', t.top + 30 + 'px');
$('.control-container').css('display', 'block');
});
$(document).on('scroll', function() {
$('.control-container').css('display', 'none');
});
.header {
background-color: maroon;
color: #fafafa;
height: 60px;
position: fixed;
width: 100%;
text-align: center;
line-height: 19px;
font-size: 25px;
z-index: 2;
padding: 0px;
margin: 0px;
top: 0;
}
.content {
background-color: #fff;
border: 1px solid #999;
padding: 5px;
height: 100%;
width: 100%;
position: absolute;
z-index: 1;
top: 60px;
}
.control-container {
width: auto;
background-color: red;
#position: fixed;
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="header">
Header
</div>
<div style="clear:both">
</div>
<div class="content">
<br/>
<br/>
<br/>
<br/>
<br/>
<button id="btn">Click Me</button>
<div class="control-container" style="display:none;">
Keep me exactly underneath 'Click Me' when Page is scrolled.
</div>
</div>
</div>
CSS position fixed property positions an element referencing view's/body's dimension.
If you have access of modifying CSS, then just remove the position: fixed; property from .control-container.
If you don't have access, then using script add position: static !important property to .control-container.
$('.control-container').css('cssText', 'position: static !important');
Modified JSFiddle

Smooth scrolling to internal links within div acting strangely; possibly a CSS sizing/spacing issue

I have a div that uses overflow:hidden and is within several other div containers. I am trying to scroll to internal links within the div using smooth scrolling via jQuery. Here is my code, which I have used on other projects with good results:
$(function() {
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('#cardb').animate({
scrollTop: target.offset(
).top
}, 0500);
return false;
}
}
});
});
.slide {
position: relative;
padding: 2vh 3vw;
min-height: 80vh;
width: 100vw;
}
#slide4 {
background: #ddd;
width: 100vw;
z-index: 1;
padding: 2.5vw;
}
#carda, #cardb {
width: 40vw;
height: 60vh;
padding: 3vw;
background: #fff;
opacity: 0.8;
float: left;
}
#cardb {
float: right;
overflow: hidden;
}
#cardb-1, #cardb-2, #cardb-3, #cardb-4 {
position: relative;
height: 60vh;
}
#linkcontainer {
position: absolute;
bottom: 20vh;
}
.linkcircle {
height: 3vh;
width: 3vh;
margin: 1vh;
background: #999;
opacity: 0.5;
transition: 0.35s;
border-radius: 1.5vh;
}
.linkcircle:hover {
opacity: 0.9;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="slide4" class="slide">
<div id="carda">
<p>CARD A</p>
</div>
<div id="cardb">
<div id="cardb-1">
<p>CARD B 1</p>
</div>
<div id="cardb-2">
<p>CARD B 2</p>
</div>
<div id="cardb-3">
<p>CARD B 3</p>
</div>
<div id="cardb-4">
<p>CARD B 4</p>
</div>
<div id="linkcontainer">
<div class="linkcircle"></div>
<div class="linkcircle"></div>
<div class="linkcircle"></div>
<div class="linkcircle"></div>
</div>
</div>
</div>
I am baffled by the results - the links almost never scroll to the correct target, and clicking the same link twice still scrolls (e.g. when you are at #cardb-1 and click the link for #cardb-1 again, the div scrolls somewhere else). I'm new to jQuery, but I've researched as much as I could figure out, with no improvement. I suspect it might actually be a CSS problem, but I'm not sure where I've gone wrong with that either. The links come up fine at the expected position when I deactivate the jQuery.
I am looking to solve this exact problem, I have taken your code as a start, so thanks.
In order to solve the scroll from happening again when clicking on the same link, I have include this code at the beginning of the function:
if( location.hash == this.hash){ return false;}
Although, it seems to take the process 1 or 2 seconds to settle in the new hash, so if you click twice on the same link withing that time lapse, the problem still persists, but if you click after that period nothing happens. I am still trying to figure out if I can eliminate the 1-2 seconds refreshing lapse, but at least it's a beginning.
Here is some working code that will scroll to the right place.
The trick is that the animated function takes an absolute y, and the original code only accounts for the top, which does not include the margins, and there is another 100px added (in my page) which I am not sure where it's coming from. So I just iterate through all divs until I reach the sought one, and calculate its position along the way. This also fixes the behavior when you click rapidly on two links.
$(function() {
$('a[href^="#"]').click(function() {
if( location.hash == this.hash){ return false;}
var target = $(this.hash);
var targetId = this.hash.substr(1);
var toGo = 0;
// class of each div that has an id for anchor link.
$('.info-box').each(function() {
var box = $(this);
if( this.id == targetId){
toGo += box.outerHeight(true)-box.outerHeight();
return false;
}
toGo += box.outerHeight(true)-box.outerHeight();
toGo += box.outerHeight();
});
if (target.length) {
// id of the container div
$('#page').animate({scrollTop: toGo}, 700);
return false;
}
});
});

How to trigger the layout to change?

I've a sticked element which gets the top-alignment from current scroll-offset. Problem is, that the layout is not "retriggerd" if the space from it is free. So there stays a ghost-gap where the sticked element was...
http://fiddle.jshell.net/pPc4V/
The markup is pretty simple:
...
as well as the js:
var $win = $(this);
var sticked = document.querySelector('a.sticked');
$win.on('scroll', function () {
var scrollTop = $win.scrollTop();
sticked.style.top = scrollTop + 'px';
// $win.resize();
});
...and the css looks good so far:
a {
display: inline-block;
width: 90px;
height: 90px;
background: deepskyblue;
}
.sticked {
position: relative;
top: 0;
left: 0;
background: tomato;
}
I tried to trigger the resize-event on scroll (as you see above uncommented), but no success! Any ideas, how to retrigger the layout so that the free-gap is filled with the next floated element?
Update
To clarify what I mean I made a simple image-timelime:
Step 1
Step 2
Step 3
The issue is that you are setting position fixed on an element which is displayed inline. That will cause that space to occur. I have redid your jsFiddle with proper alignment.
To fix it, I added the class "stuck" only when the document's scrollTop position is greater than the scrollTop position of your target element.
jsFiddle: http://fiddle.jshell.net/pPc4V/44/
HMTL:
<div id="grid">
etc...
</div>
CSS:
#grid {
height:1000px;
overflow:hidden;
float:left
}
#grid > a {
display: inline-block;
width: 90px;
height: 90px;
background: deepskyblue;
}
.stuck {
position: fixed;
background: navy !important;
}
JS:
$(window).on('scroll', function () {
var $doc = $(document),
parentElement = $('#grid'),
childToGetStuck = parentElement.find('a:nth-child(5)');
if ($doc.scrollTop() > childToGetStuck.scrollTop()) {
childToGetStuck.addClass('stuck');
//console.log($('.stuck').scrollTop())
} else {
childToGetStuck.removeClass('stuck');
}
});

Categories