jQuery on click animation only happens after first click - javascript

I have a very odd problem that i cant seem to crack, I have sliding divs like in y previous question. Now I am trying to implement a auto height feature, so far it works perfectly, the only problem i am facing is that it only animate the height of the wrapper after the first initial click.
So Basically, if you click any link the first time, the height just kind of snaps in place, but after that anything you click animates the height perfectly.
And finally IE8 is a browser i have to unfortunately support, and then when clicked the div expands super high and then just snaps back to where its meant to be.
JSFIDDLE DEMO
And here is the code:
HTML:
<nav>
page 1
page 2
page 3
</nav>
<div class="wrapper">
<div id="page-1" class="page">
<div class="page-container">
<h3>page 1</h3>
<div>Simulated content heigher than 100%</div>
</div>
</div>
<div id="page-2" class="page">
<div class="page-container">
<h3>page 2</h3>
<div>Simulated content heigher than 100%</div>
<div>Simulated content heigher than 100%</div>
<div>Simulated content heigher than 100%</div>
<div>Simulated content heigher than 100%</div>
<div>Simulated content heigher than 100%</div>
</div>
</div>
<div id="page-3" class="page">
<div class="page-container">
<h3>page 3</h3>
<div>Simulated content heigher than 100%</div>
</div>
</div>
</div>
CSS:
html, body {
height: 100%;
margin: 0;
overflow-x:hidden;
position:relative;
}
nav{
position:absolute;
top:0; left:0;
height:30px;
}
.wrapper {
background: #263729;
}
.page {
float:left;
background: #992213;
min-height: 100%;
padding-top: 30px;
}
#page-1 {
background: #0C717A;
}
#page-2 {
background: #009900;
}
#page-3 {
background: #0000FF;
}
a {
color:#FFF;
}
a.selected{
color: red;
}
JavaScript:
jQuery(document).ready(function () {
var pages = jQuery('.page'),
wrapper = jQuery('.wrapper'),
menuItems = jQuery('a.scrollitem'),
wrapperWidth = 100 * pages.length,
slideWidth = 100/pages.length;
jQuery.each(pages, function (index, value) {
var page = jQuery(this);
var pageContainer = jQuery('#'+page.attr('id')+' > .page-container');
pageContainer.data('originHeight', page.outerHeight());
});
wrapper.css({width:wrapperWidth + '%', height:'auto', marginLeft:0});
pages.width(slideWidth + '%');
menuItems.click(function(){
var menuItem = jQuery(this),
page = jQuery(menuItem.attr('href')),
pageContainer = jQuery('#'+page.attr('id')+' > .page-container'),
height = pageContainer.data('originHeight'),
slideNumber = page.index('.page'),
margin = slideNumber * -100 + '%';
menuItems.removeClass('selected');
menuItem.addClass('selected');
wrapper.animate({marginLeft: margin, height: height},{
duration: 1000,
start: function () {
page.animate({height:height},1000);
},
complete: function () {
pages.css({height:1,overflow:'hidden'});
jQuery(this).css({height:'auto'});
page.css({height:'auto',overflow:''});
}
});
return false;
});
});
Any Help Would be Fantastic.

Cracked:
http://jsfiddle.net/ugZST/2/
Just add
page.css("height", 1);
before animating the page
jQuery(document).ready(function () {
var pages = jQuery('.page'),
wrapper = jQuery('.wrapper'),
menuItems = jQuery('a.scrollitem'),
wrapperWidth = 100 * pages.length,
slideWidth = 100/pages.length;
jQuery.each(pages, function (index, value) {
var page = jQuery(this);
var pageContainer = jQuery('#'+page.attr('id')+' > .page-container');
pageContainer.data('originHeight', page.outerHeight());
});
wrapper.css({width:wrapperWidth + '%', height:'auto', marginLeft:0});
pages.width(slideWidth + '%');
menuItems.click(function(){
var menuItem = jQuery(this),
page = jQuery(menuItem.attr('href')),
pageContainer = jQuery('#'+page.attr('id')+' > .page-container'),
height = pageContainer.data('originHeight'),
slideNumber = page.index('.page'),
margin = slideNumber * -100 + '%';
menuItems.removeClass('selected');
menuItem.addClass('selected');
page.css("height", 1);
wrapper.animate({marginLeft: margin, height: height},{
duration: 1000,
start: function () {
page.animate({height:height},1000);
},
complete: function () {
pages.css({height:1,overflow:'hidden'});
jQuery(this).css({height:'auto'});
page.css({height:'auto',overflow:''});
}
});
return false;
});
});

Try this Fiddle
I just added this line during the init:
pages.css({height: $(pages[0]).height()});
Update
Just set the height of the inactive pages to 1 as the complete does: Fiddle
if (!$('a.scrollitem[href="' + '#' + page.attr('id') + '"]').hasClass('selected'))
page.css({height: 1});

I think what's happening is that the 2nd div seems to "pop" open when clicking a nav link because the height of this div is not being set explicitly at the outset, and the rendered content in this div simply takes up that amount of height, making the div that tall. Once you explicitly set the height of this div via your animation, that problem no longer exists because the explicit CSS height declaration on the div overrides this issue. For example, notice that when you click "page 3" you see the 2nd div "pop" open, but the then the height animation seems to work on the page 3 div. I think you will need to explicitly set the height on your page-container divs ahead of time, either via CSS, or at the beginning of your click handler (maybe use some overflow-y: hidden?), and potentially fix any issues this may cause in your JS height calculations.

Your js initialisation for wrapper's css seems to be wrong. Replace it with
wrapper.css({width:wrapperWidth + '%', marginLeft:0,height:pages.first().outerHeight()});
That works for me.
Add in your css
.wrapper {
background: #263729;
overflow-y: hidden
}
And that works smoothly

Related

Temporarily disable scroll at position/element with JS

I'm trying to find a way to lock the scroll at at a specified height or element for a certain amount of scrolls.
So in this plnkr I want it to stop on the second slide for 2-3 scrolls, and then proceed.
http://plnkr.co/edit/BAlFMLBhzVaqWuwhGCu8?p=preview
<div class="slide s1">S.O. made me include some code with plnkr link</div>
<div class="slide s2">Title 2</div>
<div class="slide s3">Title 3</div>
I've tried the following:
How to disable scrolling temporarily?
But if the user scrolls fast enough, they can scroll past the title.
I imagine this is because the UI thread is busy, and then when the JS finally kicks in, the title in the slide is out of view.
A good working example of what I'm looking for is here (on the second slide): http://journey.lifeofpimovie.com/
How does one achieve this effect?
I think link you have added is using some personal javascript plugins , it doesn't disable scrolling temporarily . I'm not familiar with these plugins but you can search for scrolling webpages plugins like this one : http://alvarotrigo.com/fullPage/
it has some Examples like this one and some others you can try .
Try
var $w = $(window);
var $slides = $('.slide');
$.fx.interval = -100;
var scrollTiles = function scrollTiles(e) {
var el = $slides.filter(function (i, el) {
return el.getBoundingClientRect().bottom >
parseInt($(el).css("height")) + 10
}),
// select second tile
tileId = el.prev().is($slides)
? el.prev().index()
: $slides.eq(-1).index();
// do stuff at second tile
if (tileId === 2) {
$slides.not(":eq(" + (tileId - 1) + ")")
.hide(0, function () {
$w.off("scroll.tiles");
$(this).queue("tiles", function () {
$(this).show(0)
})
// delay scroll three seconds
}).delay(3000)
.dequeue("tiles")
.promise()
.done(function (elems) {
// after three second delay ,
// scroll to third tile
$("body").animate({
scrollTop: elems.eq(-1).offset().top
}, 500, "linear", function () {
// prevent delay at second tile
// until after scroll to first tile from third tile ,
// reset `scroll.tiles` event
$w.on("scroll.t", function (e) {
if (elems.eq(0)[0].getBoundingClientRect().bottom >
elems.eq(0).height()) {
$(this).off("scroll.t")
.on("scroll.tiles", scrollTiles)
}
})
})
})
}
};
$w.on("scroll.tiles", scrollTiles);
/* Styles go here */
html,
body {
height: 100%;
}
.slide {
height: 100%;
}
.s1 {
background: red;
}
.s2 {
background: orange;
}
.s3 {
background: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<body>
<div class="slide s1">Title 1</div>
<div class="slide s2">Title 2</div>
<div class="slide s3">Title 3</div>
</body>

Div height doesn't get recalculated on resize

It works perfectly when I refresh the page, but when I resize it doesn't recalculate the div height.
JSFIDDLE
$(document).ready(function() {
var selectorsArray = ['home', 'about', 'portfolio', 'contact'];
responsiveResize(selectorsArray);
$(window).resize(function(){
var selectorsArray = ['home', 'about', 'portfolio', 'contact'];
responsiveResize(selectorsArray);
});
});
function responsiveResize(selectorsArray){
$.each(selectorsArray, function( index, value ) {
var cntcnter = $('#'+value+' .content-container');
var height = $('#'+value+' .content-container').height();
console.log(height);
cntcnter.css({'height': height+'px', 'margin-top': '-'+(height/2) +'px'});
});
}
The reason it's not working is because on the first run, you set a height to the container. On the second run, it already has a height set so it's always the same value. If the text overflows the container, it's not affecting the new height in this case.
If you want to keep using your code, you need to clear/remove the height you added on the previous run.
You can use this
var height = $('#'+value+' .content-container').css('height', '').height();
Like Chris Empx mentioned, this is easily obtainable with CSS.
Also, you could optimize the code like this fiddle
i wonder why you want to do that with jquery.
i would do that in css.
like that
<div class='container'>
<div class='column col 12'>
<p>Your Inputs here</p>
</div>
<div class='clear'></div>
</div>
then this in css
.container {
margin: 0 auto;
width: 960px;
}
.col-12{
width: 100%;
}
.clear {clear: both;}
.column {padding: 0 .8em; float:left;}
col-6 would be width:50%
col-3 would be width:25%

Bring an element to top of the page even if the page does not scroll

Background:
Let's say you have a simple page which has a logo and a heading only and one paragraph
<img src="http://blog.stackoverflow.com/wp-content/uploads/StackExchangeLogo1.png">
<h1>Foo Bar</h1>
<p>ABC12345</p>
This is how that looks like
That page, obviously would not have vertical overflow / scroll bar for almost even tiny scale mobile devices, let alone computers.
Question
How can you bring that heading to the top left of the screen and move the logo out of focus unless someone scrolls up? Open to using any JavaScript library and any CSS framework
Attempts:
Tried using anchors but they only work if the page already had a scroll bar and anchor was out of focus.
Tried window.scrollTo but that also requires the page to have scroll already
Tried $("html, body").animate({ scrollTop: 90}, 100); but that also doesn't work when the page doesn't have overflow
Notes:
Please note that adding some extra <br/> to induce an overflow is not the way to go, it can be done that way but that's a very ordinary workaround
Why is it needed?
Its for a form for mobile devices, simple requirement is to take the first field of the form to top of the page and hide the logo (one can scroll up if they wish to see it) so it doesn't take attention away. Not using jQueryMobile for this particular task.
If you want the user to be able to scroll up and see the logo, then the logo must be within the top boundary of the body tag, because anything outside of that tag will not be viewable. This means you cannot use negative margins or offsetting like that. The only way to achieve this is to have the page scroll to the desired location that is within the top boundary of the body tag. You can set the time for this event to one millisecond, but there will still be a 'jump' in the page when it is loaded. So, the logic is: first make sure the page is long enough to scroll to the right place, then scroll there.
//Change the jQuery selectors accordingly
//The minimum height of the page must be 100% plus the height of the image
$('body').css('min-height',$(document).height() + $('img').height());
//Then scroll to that location with a one millisecond interval
$('html, body').animate({scrollTop: $('img').height() + 'px'}, 1);
View it here.
Alternatively, you can load the page without the image in the first place. Then your form field will be flush with the top of the document. Then you could create the element at the top and similarly scroll the page again. This is a round-a-bout way of doing the same thing though. And the page will still 'jump,' there is no way around that.
Only CSS and anchor link solution
With a pseudo element :
--- DEMO ---
First :
set : html,body{height:100%;}
Second :
Choose one of your existing tags. This tag mustn't have a relatively positioned parent (except if it is the body tag). Preferably the first element in the markup displayed after the logo. For your example it would be the h1 tag. And give it this CSS :
h1:before {
content:"";
position:absolute;
height:100%;
width:1px;
}
This creates an element as heigh as the viewport area. As it is displayed under the logo, the vertical scroll lenght is the same as the logo height.
Third :
Give the first element after logo an id (for this example I gave id="anchor").
Then you can use a link like this your_page_link#anchor and you will automaticaly scroll to the anchor (logo outside/above the viewport).
This works whatever height the logo is.
link to editable fiddle
Full code :
HTML
<img src="http://blog.stackoverflow.com/wp-content/uploads/StackExchangeLogo1.png">
<h1 id="anchor">Foo Bar</h1>
<p>ABC12345</p> Anchor link
CSS
html, body {
height:100%;
margin:0;
padding:0;
}
h1:before {
content:"";
position:absolute;
width:1px;
left:0;
height:100%;
}
You might need to add js functionality to hide the logo if user scrolls down but I guess following code will fullfill the first requirement.
Please see
<script src="js/jquery.js"></script>
<img id='logo' src="http://blog.stackoverflow.com/wp-content/uploads/StackExchangeLogo1.png" style="display:none">
<h1>Foo Bar</h1>
<p>ABC12345</p>
<script type="text/javascript">
var p = $( "p:first" );
var isScrolled=false;
/* For Firfox*/
$('html').on ('DOMMouseScroll', function (e) {
isScrolled = true;
if(p.scrollTop()==0 && isScrolled==true){
$('#logo').css('display','block');
}
});
/* For Chrome, IE, Opera and Safari: */
$('html').on ('mousewheel', function (e) {
isScrolled = true;
if(p.scrollTop()==0 && isScrolled==true){
$('#logo').css('display','block');
}
});
</script>
I have referred this question to find solution.
You could use touchmove event to detect swipe up or down. This is my example. You can try it on mobile device.
<style>
#logo {
position: absolute;
top: -100%;
-webkit-transition: top 0.5s;
-moz-transition: top 0.5s;
-ms-transition: top 0.5s;
-o-transition: top 0.5s;
transition: top 0.5s;
}
#logo.show {
top: 0;
}
</style>
<script>
var perY;
var y;
$(window).on('touchmove', function(e) {
e.preventDefault();
y = window.event.touches[0].pageY;
if(!perY)
perY = y;
else
{
if(y > perY)
$('#logo').addClass('show');
else
$('#logo').removeClass('show');
perY = null;
}
});
</script>
<img id="logo" src="http://blog.stackoverflow.com/wp-content/uploads/StackExchangeLogo1.png">
<h1>Foo Bar</h1>
<p>ABC12345</p>
This is the same problem i've encountered hiding the addressbar without the page overflowing. The only solution that fitted my needs was the following:
Set the min-height of the body to the viewportheight + your logo.
$('body').css('min-height', $(window).height() + 200);
This is a simple solution of getting the height of the contents to see if we can scroll to the part of the header, if not, we add height to the paragraph.
<img id="img" src="http://blog.stackoverflow.com/wp-content/uploads/StackExchangeLogo1.png" />
<h1 id="h" >Foo Bar</h1>
<p id="par" style="background:yellow;">
hello world
</p>
script:
function hola(){
var imgH = $("#img").outerHeight(true);
var titleH = $("#h").outerHeight(true);
var winH = $(window).height();
var parH = $('#par').outerHeight(true);
var contH = (imgH + titleH + parH);
var wishH = (imgH + winH);
console.log("wished height: " + wishH);
console.log("window height: " + winH);
console.log("content height: " + contH);
if(contH < wishH){
console.log("window is smaller than desired :(");
var newH = wishH - contH;
$("#par").height(parH + newH);
$(window).scrollTop(imgH);
}
}
Here is the working example:
http://jsfiddle.net/Uup62/1/
You may like this solution: http://jsfiddle.net/jy8pT/1/
HTML:
<div class="addScroll"></div>
<h1 class="logo"><img src="https://drupal.org/files/images/OQAAAI1PPrJY0nBALB7mkvju3mkQXqLmzMhxEjeb4gp8aujEUQcLfLyy-Sn4gZdkAas6-k8eYbQlGDE-GCjKfF5gIrUA15jOjFfLRv77VBd5t-WfZURdP9V3PdmT.png" height="100" alt="company logo"/></h1>
<h2>This is a sample page heading.</h2>
<p>This is a sample page text.</p>
JS:
function addScroll()
{
$(".addScroll").css({
"height": ($(window).height()+1) + "px",
"width": "100%"
});
}
$(document).ready(function(){
addScroll();
$(window).resize(function(){
addScroll();
});
$(window).scroll(function(){
if($(window).scrollTop() > 0)
{
$(".logo").animate({
marginTop: "-110px"
}, 500);
}
if($(window).scrollTop() == 0)
{
$(".logo").animate({
marginTop: "0"
}, 500);
}
});
});
CSS:
body
{
padding:0;
margin:0;
}
h1.logo
{
display:block;
margin:0 0 10px 0;
padding:0;
outline:0;
}
.addScroll
{
position:absolute;
display:block;
top:0;
left:0;
z-index:-1;
}

how to "fix" container element height when scrollbar shows?

I have a horizontal scrollbar which will only show when hover. But the problem is when it shows, it will increase the height of it's container and push the following elements down.
the demo examle.you can see the two div will be pushed down when the scrollbar shows
<div class="wrapper">
<div class="one">
<div class="oneChild">the height is unknown,the height is unknown,the height is unknown,the height is unknown</div>
</div>
<div class="two">I'm two</div>
There are are some rules:
The wrapper and one height can't be fixed,because the *oneChild * content height is unkonwn.And the height of both are all decided by their children.
The scroball only show when hover.you can use js or css to control it's visible.
Any js/css solution will be welcome.
If jquery is an option:
$(document).ready(function() {
height = $('.one').height();
$('.two').css('marginTop', height + 'px');
});
css:
.one {
width: 100px;
border: 1px seagreen solid;
position:relative;
}
.one:hover {
overflow: scroll;
}
.two {
position: absolute;
top:30px;
}
If the child of one will never change, then you could get the height of the element after a height has been set, and then fix it at that height.
var $one = $('.one');
var height = $one.height();
$one.css('height', height);
If the height of the child of one may change, you could fix the height when you are hovering over the element, and set it to auto when you stop hovering.
var $one = $('.one');
var height = $one.height();
$one.on('mouseenter', function() {
$one.css('height', height);
});
$one.on('mouseleave', function() {
$one.css('height', 'auto');
});
You can set the height of the div on hover to the height when the scrollbar isn't there. Assuming that jQuery is ok:
$(document).ready(function() {
var oneHeight = $('.one').height();
$('.one').hover(
function(){ $(this).height(oneHeight); },
function(){ $(this).height(oneHeight); }
);
});
This does add an unfortunate vertical scrollbar, but if you want the vertical position to stay the same, you need to have the vertical bar to see the content that the horizontal scroll bar.
My solution is:add padding-bottom first ,it is as high as horizontal scroball,then when mouseover event happen,padding-bottom set zero.

How to scroll to an element inside a div?

I have a scrolled div and I want to have an event when I click on it, it will force this div to scroll to view an element inside.
I wrote its JavasSript like this:
document.getElementById(chr).scrollIntoView(true);
but this scrolls all the page while scrolling the div itself.
How to fix that?
I want to say it like this:
MyContainerDiv.getElementById(chr).scrollIntoView(true);
You need to get the top offset of the element you'd like to scroll into view, relative to its parent (the scrolling div container):
var myElement = document.getElementById('element_within_div');
var topPos = myElement.offsetTop;
The variable topPos is now set to the distance between the top of the scrolling div and the element you wish to have visible (in pixels).
Now we tell the div to scroll to that position using scrollTop:
document.getElementById('scrolling_div').scrollTop = topPos;
If you're using the prototype JS framework, you'd do the same thing like this:
var posArray = $('element_within_div').positionedOffset();
$('scrolling_div').scrollTop = posArray[1];
Again, this will scroll the div so that the element you wish to see is exactly at the top (or if that's not possible, scrolled as far down as it can so it's visible).
You would have to find the position of the element in the DIV you want to scroll to, and set the scrollTop property.
divElem.scrollTop = 0;
Update:
Sample code to move up or down
function move_up() {
document.getElementById('divElem').scrollTop += 10;
}
function move_down() {
document.getElementById('divElem').scrollTop -= 10;
}
Method 1 - Smooth scrolling to an element inside an element
var box = document.querySelector('.box'),
targetElm = document.querySelector('.boxChild'); // <-- Scroll to here within ".box"
document.querySelector('button').addEventListener('click', function(){
scrollToElm( box, targetElm , 600 );
});
/////////////
function scrollToElm(container, elm, duration){
var pos = getRelativePos(elm);
scrollTo( container, pos.top , 2); // duration in seconds
}
function getRelativePos(elm){
var pPos = elm.parentNode.getBoundingClientRect(), // parent pos
cPos = elm.getBoundingClientRect(), // target pos
pos = {};
pos.top = cPos.top - pPos.top + elm.parentNode.scrollTop,
pos.right = cPos.right - pPos.right,
pos.bottom = cPos.bottom - pPos.bottom,
pos.left = cPos.left - pPos.left;
return pos;
}
function scrollTo(element, to, duration, onDone) {
var start = element.scrollTop,
change = to - start,
startTime = performance.now(),
val, now, elapsed, t;
function animateScroll(){
now = performance.now();
elapsed = (now - startTime)/1000;
t = (elapsed/duration);
element.scrollTop = start + change * easeInOutQuad(t);
if( t < 1 )
window.requestAnimationFrame(animateScroll);
else
onDone && onDone();
};
animateScroll();
}
function easeInOutQuad(t){ return t<.5 ? 2*t*t : -1+(4-2*t)*t };
.box{ width:80%; border:2px dashed; height:180px; overflow:auto; }
.boxChild{
margin:600px 0 300px;
width: 40px;
height:40px;
background:green;
}
<button>Scroll to element</button>
<div class='box'>
<div class='boxChild'></div>
</div>
Method 2 - Using Element.scrollIntoView:
Note that browser support isn't great for this one
var targetElm = document.querySelector('.boxChild'), // reference to scroll target
button = document.querySelector('button'); // button that triggers the scroll
// bind "click" event to a button
button.addEventListener('click', function(){
targetElm.scrollIntoView()
})
.box {
width: 80%;
border: 2px dashed;
height: 180px;
overflow: auto;
scroll-behavior: smooth; /* <-- for smooth scroll */
}
.boxChild {
margin: 600px 0 300px;
width: 40px;
height: 40px;
background: green;
}
<button>Scroll to element</button>
<div class='box'>
<div class='boxChild'></div>
</div>
Method 3 - Using CSS scroll-behavior:
.box {
width: 80%;
border: 2px dashed;
height: 180px;
overflow-y: scroll;
scroll-behavior: smooth; /* <--- */
}
#boxChild {
margin: 600px 0 300px;
width: 40px;
height: 40px;
background: green;
}
<a href='#boxChild'>Scroll to element</a>
<div class='box'>
<div id='boxChild'></div>
</div>
Native JS, Cross Browser, Smooth Scroll (Update 2020)
Setting ScrollTop does give the desired result but the scroll is very abrupt. Using jquery to have smooth scroll was not an option. So here's a native way to get the job done that supports all major browsers. Reference - caniuse
// get the "Div" inside which you wish to scroll (i.e. the container element)
const El = document.getElementById('xyz');
// Lets say you wish to scroll by 100px,
El.scrollTo({top: 100, behavior: 'smooth'});
// If you wish to scroll until the end of the container
El.scrollTo({top: El.scrollHeight, behavior: 'smooth'});
That's it!
And here's a working snippet for the doubtful -
document.getElementById('btn').addEventListener('click', e => {
e.preventDefault();
// smooth scroll
document.getElementById('container').scrollTo({top: 175, behavior: 'smooth'});
});
/* just some styling for you to ignore */
.scrollContainer {
overflow-y: auto;
max-height: 100px;
position: relative;
border: 1px solid red;
width: 120px;
}
body {
padding: 10px;
}
.box {
margin: 5px;
background-color: yellow;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
}
#goose {
background-color: lime;
}
<!-- Dummy html to be ignored -->
<div id="container" class="scrollContainer">
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div id="goose" class="box">goose</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
</div>
<button id="btn">goose</button>
Update: As you can perceive in the comments, it seems that Element.scrollTo() is not supported in IE11. So if you don't care about IE11 (you really shouldn't, Microsoft is retiring IE11 in June 2022), feel free to use this in all your projects. Note that support exists for Edge! So you're not really leaving your Edge/Windows users behind ;)
Reference
To scroll an element into view of a div, only if needed, you can use this scrollIfNeeded function:
function scrollIfNeeded(element, container) {
if (element.offsetTop < container.scrollTop) {
container.scrollTop = element.offsetTop;
} else {
const offsetBottom = element.offsetTop + element.offsetHeight;
const scrollBottom = container.scrollTop + container.offsetHeight;
if (offsetBottom > scrollBottom) {
container.scrollTop = offsetBottom - container.offsetHeight;
}
}
}
document.getElementById('btn').addEventListener('click', ev => {
ev.preventDefault();
scrollIfNeeded(document.getElementById('goose'), document.getElementById('container'));
});
.scrollContainer {
overflow-y: auto;
max-height: 100px;
position: relative;
border: 1px solid red;
width: 120px;
}
body {
padding: 10px;
}
.box {
margin: 5px;
background-color: yellow;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
}
#goose {
background-color: lime;
}
<div id="container" class="scrollContainer">
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div id="goose" class="box">goose</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
<div class="box">duck</div>
</div>
<button id="btn">scroll to goose</button>
Code should be:
var divElem = document.getElementById('scrolling_div');
var chElem = document.getElementById('element_within_div');
var topPos = divElem.offsetTop;
divElem.scrollTop = topPos - chElem.offsetTop;
You want to scroll the difference between child top position and div's top position.
Get access to child elements using:
var divElem = document.getElementById('scrolling_div');
var numChildren = divElem.childNodes.length;
and so on....
If you are using jQuery, you could scroll with an animation using the following:
$(MyContainerDiv).animate({scrollTop: $(MyContainerDiv).scrollTop() + ($('element_within_div').offset().top - $(MyContainerDiv).offset().top)});
The animation is optional: you could also take the scrollTop value calculated above and put it directly in the container's scrollTop property.
We can resolve this problem without using JQuery and other libs.
I wrote following code for this purpose:
You have similar structure ->
<div class="parent">
<div class="child-one">
</div>
<div class="child-two">
</div>
</div>
JS:
scrollToElement() {
var parentElement = document.querySelector('.parent');
var childElement = document.querySelector('.child-two');
parentElement.scrollTop = childElement.offsetTop - parentElement.offsetTop;
}
We can easily rewrite this method for passing parent and child as an arguments
Another example of using jQuery and animate.
var container = $('#container');
var element = $('#element');
container.animate({
scrollTop: container.scrollTop = container.scrollTop() + element.offset().top - container.offset().top
}, {
duration: 1000,
specialEasing: {
width: 'linear',
height: 'easeOutBounce'
},
complete: function (e) {
console.log("animation completed");
}
});
None of other answer fixed my issue.
I played around with scrollIntoView arguments and managed to found a solution. Setting inline to start and block to nearest prevents parent element (or entire page) to scroll:
document.getElementById(chr).scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start'
});
There are two facts :
1) Component scrollIntoView is not supported by safari.
2) JS framework jQuery can do the job like this:
parent = 'some parent div has css position==="fixed"' || 'html, body';
$(parent).animate({scrollTop: $(child).offset().top}, duration)
Here's a simple pure JavaScript solution that works for a target Number (value for scrollTop), target DOM element, or some special String cases:
/**
* target - target to scroll to (DOM element, scrollTop Number, 'top', or 'bottom'
* containerEl - DOM element for the container with scrollbars
*/
var scrollToTarget = function(target, containerEl) {
// Moved up here for readability:
var isElement = target && target.nodeType === 1,
isNumber = Object.prototype.toString.call(target) === '[object Number]';
if (isElement) {
containerEl.scrollTop = target.offsetTop;
} else if (isNumber) {
containerEl.scrollTop = target;
} else if (target === 'bottom') {
containerEl.scrollTop = containerEl.scrollHeight - containerEl.offsetHeight;
} else if (target === 'top') {
containerEl.scrollTop = 0;
}
};
And here are some examples of usage:
// Scroll to the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget('top', scrollableDiv);
or
// Scroll to 200px from the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget(200, scrollableDiv);
or
// Scroll to targetElement
var scrollableDiv = document.getElementById('scrollable_div');
var targetElement= document.getElementById('target_element');
scrollToTarget(targetElement, scrollableDiv);
given you have a div element you need to scroll inside, try this piece of code
document.querySelector('div').scroll(x,y)
this works with me inside a div with a scroll, this should work with you in case you pointed the mouse over this element and then tried to scroll down or up. If it manually works, it should work too
User Animated Scrolling
Here's an example of how to programmatically scroll a <div> horizontally, without JQuery. To scroll vertically, you would replace JavaScript's writes to scrollLeft with scrollTop, instead.
JSFiddle
https://jsfiddle.net/fNPvf/38536/
HTML
<!-- Left Button. -->
<div style="float:left;">
<!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
<input type="button" value="«" style="height: 100px;" onmousedown="scroll('scroller',3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
<!-- <3 -->
<img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
<!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
<input type="button" value="»" style="height: 100px;" onmousedown="scroll('scroller',-3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>
JavaScript
// Declare the Shared Timer.
var TIMER_SCROLL;
/**
Scroll function.
#param id Unique id of element to scroll.
#param d Amount of pixels to scroll per sleep.
#param del Size of the sleep (ms).*/
function scroll(id, d, del){
// Scroll the element.
document.getElementById(id).scrollLeft += d;
// Perform a delay before recursing this function again.
TIMER_SCROLL = setTimeout("scroll('"+id+"',"+d+", "+del+");", del);
}
Credit to Dux.
Auto Animated Scrolling
In addition, here are functions for scrolling a <div> fully to the left and right. The only thing we change here is we make a check to see if the full extension of the scroll has been utilised before making a recursive call to scroll again.
JSFiddle
https://jsfiddle.net/0nLc2fhh/1/
HTML
<!-- Left Button. -->
<div style="float:left;">
<!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
<input type="button" value="«" style="height: 100px;" onclick="scrollFullyLeft('scroller',3, 10);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
<!-- <3 -->
<img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
<!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
<input type="button" value="»" style="height: 100px;" onclick="scrollFullyRight('scroller',3, 10);"/>
</div>
JavaScript
// Declare the Shared Timer.
var TIMER_SCROLL;
/**
Scroll fully left function; completely scrolls a <div> to the left, as far as it will go.
#param id Unique id of element to scroll.
#param d Amount of pixels to scroll per sleep.
#param del Size of the sleep (ms).*/
function scrollFullyLeft(id, d, del){
// Fetch the element.
var el = document.getElementById(id);
// Scroll the element.
el.scrollLeft += d;
// Have we not finished scrolling yet?
if(el.scrollLeft < (el.scrollWidth - el.clientWidth)) {
TIMER_SCROLL = setTimeout("scrollFullyLeft('"+id+"',"+d+", "+del+");", del);
}
}
/**
Scroll fully right function; completely scrolls a <div> to the right, as far as it will go.
#param id Unique id of element to scroll.
#param d Amount of pixels to scroll per sleep.
#param del Size of the sleep (ms).*/
function scrollFullyRight(id, d, del){
// Fetch the element.
var el = document.getElementById(id);
// Scroll the element.
el.scrollLeft -= d;
// Have we not finished scrolling yet?
if(el.scrollLeft > 0) {
TIMER_SCROLL = setTimeout("scrollFullyRight('"+id+"',"+d+", "+del+");", del);
}
}
This is what has finally served me
/** Set parent scroll to show element
* #param element {object} The HTML object to show
* #param parent {object} The HTML object where the element is shown */
var scrollToView = function(element, parent) {
//Algorithm: Accumulate the height of the previous elements and add half the height of the parent
var offsetAccumulator = 0;
parent = $(parent);
parent.children().each(function() {
if(this == element) {
return false; //brake each loop
}
offsetAccumulator += $(this).innerHeight();
});
parent.scrollTop(offsetAccumulator - parent.innerHeight()/2);
}
I needed to scroll a dynamically loading element on a page so my solution was a little more involved.
This will work on static elements that are not lazy loading data and data being dynamically loaded.
const smoothScrollElement = async (selector: string, scrollBy = 12, prevCurrPos = 0) => {
const wait = (timeout: number) => new Promise(resolve => setTimeout(resolve, timeout));
const el = document.querySelector(selector) as HTMLElement;
let positionToScrollTo = el.scrollHeight;
let currentPosition = Math.floor(el.scrollTop) || 0;
let pageYOffset = (el.clientHeight + currentPosition);
if (positionToScrollTo == pageYOffset) {
await wait(1000);
}
if ((prevCurrPos > 0 && currentPosition <= prevCurrPos) !== true) {
setTimeout(async () => {
el.scrollBy(0, scrollBy);
await smoothScrollElement(selector, scrollBy, currentPosition);
}, scrollBy);
}
};
browser does scrolling automatically to an element that gets focus, so what you can also do it to wrap the element that you need to be scrolled to into <a>...</a> and then when you need scroll just set the focus on that a

Categories

Resources