fixed side scrollbar for case study - javascript

This is my first post. I am new to code and am trying to code my case studies for my portfolio. I want to include a "scroll bar" to work as a sort of timeline on the left side that is "fixed" so as you scroll down the page it fills up. This website has a something close to what I am looking to accomplish. they have a small red "scroll bar" on the left side.
https://edesigninteractive.com/

A simple way is to divide your scroll position by the height of the body object.
The height of the body object can be obtained with var numerator = document.body.scrollTop.
Your scroll position is slightly more tricky. You will want the height of the element minus the might of the view port. This can be done with
var denominator = document.documentElement.scrollHeight - document.documentElement.clientHeight
from there you you set up an event listener that sets the height of your scrollbar element as a percentage of the viewheight when scrolling.
document.addEventListener('scroll', function(e) {
document.getElementById('scrollBar').style.height = numerator/denominator + '%'
});
There might be better ways to do this, but this is the first one to come to my mind.

I modified alleynraven answer, and put it into codepen.
var $scrollEl = document.querySelector('.scroll-progress')
document.addEventListener('scroll', function(e) {
var currentPos = document.documentElement.scrollTop;
var scrollPos = document.documentElement.scrollHeight - document.documentElement.clientHeight;
$scrollEl.style.height = (currentPos / scrollPos * 100) + '%';
});
.scroll-progress {
position: fixed;
left: 0;
top: 0;
width: 8px;
background-color: rgb(248 113 113);
}
.bg-blue {
background-color: rgb(186 230 253);
}
.bg-teal {
background-color: rgb(153 246 228);
}
.bg-lime {
background-color: rgb(217 249 157);
}
.bg-yellow {
background-color: rgb(254 240 138);
}
<div class="scroll-progress"></div>
<div class="content">
<div class="bg-blue" style="height: 1000px;"></div>
<div class="bg-teal" style="height: 1000px;"></div>
<div class="bg-lime" style="height: 1000px;"></div>
<div class="bg-yellow" style="height: 1000px;"></div>
</div>

Related

Make Header/Navigation change colour when on different section of the website

I am working on a website redesign for my personal portfolio website. I had a cool feature in mind where my header/navigation bar would change colour depending on what section of the webpage it is on (The website is one page only).
The only way i could think of doing this is adding onclick events to the links that go to the different sections of the page, however this would not allow me to change the colour of the header/navigation bar for when the user scrolls manually to a new section.
I was wondering if someone could point me in the right direction as I'm not sure where to start.
Here is the website as it stands now if people want to view it:
www.kylebinger.com
Here is my HTML markup regarding the header
<header>
<div class="container">
<nav>
Welcome
Work
Case Study
About
Contact
</nav>
</div>
</header>
Thanks in advance for any help.
JQuery's offset and scrollTop functions should do the trick. .offset() gets the current coordinates of the element, while .scrollTop() will get the current scrollbar position. Compare them and change CSS when conditions are met. See example:
var top1 = $('#home').offset().top;
var top2 = $('#featuredWork').offset().top;
var top3 = $('#caseStudy').offset().top;
$(document).scroll(function() {
var scrollPos = $(document).scrollTop();
if (scrollPos >= top1 && scrollPos < top2) {
$('#change').css('background-color', '#f00');
} else if (scrollPos >= top2 && scrollPos < top3) {
$('#change').css('background-color', '#0f0');
} else if (scrollPos >= top3) {
$('#change').css('background-color', '#00f');
}
});
body {
margin: 0;
padding-top: 30px
}
header {
position: fixed;
top: 0;
width: 100%;
height: 30px;
background-color: #000;
}
section {
height: 500px;
background-color: #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header id="change">
<div class="container">
<nav>
Welcome
Work
Case Study
</nav>
</div>
</header>
<section id="home">Content</section>
<section id="featuredWork">Content</section>
<section id="caseStudy">Content</section>

Finding where element meets top of scrollable div

I have a scrollable div container fits multiple "pages" (or div's) inside of it's container.
My goal is to, at any given moment, figure out where inside my red container does it reach the top of my scrollable container. So it can be a constant on scroll event, or a button that triggers this task.
So for example. If I have a absolute div element inside one of my red boxes at top:50px. And if I scroll to where that div element reaches the top of my scrollable container. The trigger should say that I am at 50px of my red container.
I'm having a hard time grasping how to accomplish this but I've tried things like:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop());
});
But it doesn't take into account the separate pages and I don't believe it it completely accurate depending on the scale. Any guidance or help would be greatly appreciated.
Here is my code and a jsfiddle to better support my question.
Note: If necessary, I use scrollspy in my project so I could target which red container needs to be checked.
HTML
<div id="pageContent" class="slide" style="background-color: rgb(241, 242, 247); height: 465px;">
<div id="formBox" style="height: 9248.627450980393px;">
<div class="trimSpace" style="width: 1408px; height: 9248.627450980393px;">
<div id="formScale" style="width: 816px; -webkit-transform: scale(1.7254901960784315); display: block;">
<form action="#" id="XaoQjmc0L51z_form" autocomplete="off">
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_1">
<div class="formContent">
<div class="formBackground">
<div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
</div>
</div>
</div>
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_2">
<div class="formContent">
<div class="formBackground"><div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">This should still say 50px</div></div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
CSS
#pageContent {
position:relative;
padding-bottom:20px;
background-color:#fff;
z-index:2;
overflow:auto;
-webkit-overflow-scrolling: touch;
-webkit-transform: translate(0, 0);
-moz-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0);
}
#formBox {
display: block;
position: relative;
height: 100%;
padding: 15px;
}
.trimSpace {
display: block;
position: relative;
margin: 0 auto;
padding: 0;
height: 100%;
overflow: hidden;
}
#formScale::after {
display: block;
content:'';
padding-bottom:5px;
}
#formScale {
position:relative;
width:816px;
margin:0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
}
.formContainer {
position:relative;
margin : 0 auto 15px auto;
padding:0;
}
.formContent {
position:absolute;
width:100%;
height:100%;
}
.formBackground {
width:100%;
height:100%;
background-color:red;
}
JS
var PAGEWIDTH = 816;
$(window).resize(function (e) {
zoomProject();
resize();
});
function resize() {
$("#pageContent").css('height', window.innerHeight - 45 + 'px');
}
function zoomProject() {
var maxWidth = $("#formBox").width(),
percent = maxWidth / PAGEWIDTH;
$("#formScale").css({
'transform': 'scale(' + percent + ')',
'-moz-transform': 'scale(' + percent + ')',
'-webkit-transform': 'scale(' + percent + ')',
'-ms-transform': 'scale(' + percent + ')'
});
$(".trimSpace").css('width', (PAGEWIDTH * percent) + 'px');
$("#formBox, .trimSpace").css('height', ($("#formScale").height() * percent) + 'px');
}
zoomProject();
resize();
EDIT:
I don't think I am conveying a good job at relaying what I want to accomplish.
At the moment there are two .formContainer's. When I scroll #pageContainer, the .formContainer divs move up through #pageContainer.
So what I want to accomplish is, when a user clicks the "ME" button or #click (as shown in the fiddle below), I'd like to know where in that particular .formContainer, is it touching the top of #pageContainer.
I do use scroll spy in my real world application so I know which .formContainer is closest to the top. So if you just want to target one .formContainer, that is fine.
I used these white div elements as an example. If I am scrolling #pageContainer, and that white div element is at the top of screen as I am scrolling and I click on "ME", the on click trigger should alert to me that .formContainer is touching the top of #pageContainer at 50px from the top. If, the the red container is just touching the top of #pageContainer, it should say it is 0px from the top.
I hope that helps clear up some misconception.
Here is an updated jsfiddle that shows the kind of action that I want to happen.
I am giving this a stab because I find these things interesting. It might just be a starting point since I have a headache today and am not thinking straight. I'd be willing to bet it can be cleaned up and simplified some.
I also might be over-complicating the approach I took, getting the first visible form, and the positioning. I didn't use the getBoundingClientRect function either.
Instead, I approached it trying to account for padding and margin, using a loop through parent objects up to the pageContent to get the offset relative to that element. Because the form is nested a couple levels deep inside the pageContent element you can't use position(). You also can't use offset() since that changes with scroll. The loop approach allowed me to factor the top margin/padding in. I haven't looked at the other solutions proposed fully so there might be a shorter way to accomplish this.
Keeping in mind that the scale will affect the ACTUAL location of the child elements, you have to divide by your scale percentage when getting the actual location. To do that I moved the scalePercentage to a global var so it was usable by the zoom function and the click.
Here's the core of what I did. The actual fiddle has more logging and junk:
var visForm = getVisibleForm();
var formTop = visForm.position().top;
var parents = visForm.parentsUntil('#pageContent');
var truOffset = 0;
parents.each(function() {
truOffset -= $(this).position().top;
});
// actual location of form relative to pageContent visible pane
var formLoc = truOffset - formTop;
var scaledLoc = formLoc / scalePercent;
Updated Fiddle (forgot to account for scale in get func): http://jsfiddle.net/e6vpq9c8/5/
If I understand your question correctly, what you want is to catch when certain descendant elements reach the top of the outer container, and then determine the position of the visible "page" (div with class formContainer) relative to the top.
If so, the first task is to mark the specific elements that could trigger this:
<div class='triggerElement' style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
Then the code:
// arbitrary horizontal offset - customize for where your trigger elements are placed horizontally
var X_OFFSET = 100;
// determine once, at page load, where outer container is on the page
var outerContainerRect;
$(document).ready(function() {
outerContainerRect = $("#pageContent").get(0).getBoundingClientRect();
});
// when outer container is scrolled
$("#pageContent").scroll(function(e) {
// determine which element is at the top
var topElement = $(document.elementFromPoint(outerContainerRect.left+X_OFFSET, outerContainerRect.top));
/*
// if a trigger element
if (topElement.hasClass("triggerElement")) {
// get trigger element's position relative to page
console.log(topElement.position().top);
}
*/
var page = topElement.closest(".formContainer");
if (page.length > 0) {
console.log(-page.get(0).getBoundingClientRect().top);
}
});
EDIT: Changed code to check formContainer elements rather than descendant elements, as per your comment.
http://jsfiddle.net/j6ybgf58/23/
EDIT #2: A simpler approach, given that you know which formContainer to target:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop() - $("#xzOwqphM4GGR_1").position().top);
});
http://jsfiddle.net/rL4Ly3yy/5/
However, it still gives different results based on the size of the window. This seems unavoidable - the zoomProject and resize functions are explicitly resizing the content, so you would have to apply the inverse transforms to the number you get from this code if you want it in the original coordinate system.
I do not fully understand what it is that you are needing, but if i am correct this should do the trick
$("#pageContent").scroll(function(e) {
// If more then 50 pixels from the top has been scrolled
// * if you want it to only happen at 50px, just execute this once by removing the scroll listener on pageContent
if((this.scrollHeight - this.scrollTop) < (this.scrollHeight - 50)) {
alert('it is');
}
});
ScrollHeight is the full height of the object including scrollable pixels.
ScrollTop is the amount of pixels scrolled from the top.
You can use waypoints to detect the position of divs based on where you're scrolling.
Here is a link to their official website's example: http://imakewebthings.com/waypoints/shortcuts/inview/

CSS: dynamic width div

I'm coding a slider and I have problems with the stylying of the container.
I have 3 div:
A div that sets the width and height of the slider
A container div with all the content divs (and the scroll for the slider)
Many divthat show different contents each
What I want to do is apply a negative margin on the second div to slide the content.
LIVE example: http://jsbin.com/efuyix/7/edit
JS:
function animate(element) {
var start = new Date();
var id = setInterval(function() {
var timePassed = new Date() - start;
var progress = timePassed / 600;
if (progress > 1) progress = 1;
element.style.marginLeft = -50 * Math.pow(progress, 5)+"px";
if (progress == 1) {
clearInterval(id);
}
}, 10);
}
CSS
.example_path {
overflow: hidden;
width: 50px;
height: 50px;
}
.example_block {
min-width: 100px;
height: 50px;
float:left;
}
.example_in_block {
width: 50px;
height: 50px;
float:left;
}
HTML
<div class="example_path">
<div class="example_block" onclick="animate(this)">
<div class="example_in_block" style="background-color:blue;"></div>
<div class="example_in_block" style="background-color:pink;"></div>
<div style="clear:both;"></div>
</div>
</div>
The problem:
The width of .example_block has to be exactly the same or more than (amount of content divs .example_block * 50 [width size of content div] ) to work.
For example, if I set the width size of the .example_block to 90, the pink div will be below the blue div and not beside it.
I want the container div to be dynamic so I don't have to set the specific width size.
How can I do this?
Simply remove the float:left in the .example_block.
See http://jsbin.com/efuyix/9/edit
Not with negative margins. You can probably set padding on one of the outer DIVs.
Also, min-width isn't going to be backwards compatible with older versions of IE.
Check this example: http://jsfiddle.net/5xBYN/6/
If the initial positioning is good, you can then use negative values on your container DIV (the third DIV) for top, left, right or bottom to achieve sliding.
Update:
Maybe this is closer to what you want. http://jsfiddle.net/5xBYN/7/
I'm still not sure what you are trying to do. Maybe edit the fiddle I posted and update your question with what I'm getting wrong if there is anything.

jQuery: Animating width/height, but Stay Centered

I have an element on the page that I've already centered horizontally and vertically (It's a jQuery UI Modal Dialog), and want to resize it using .animate() like this:
<div id="element" style="width: 100px; height: 100px;">
Hi Stack Overflow!
</div>
<script type="text/javascript">
$('#element').animate({ height: "200px" });
</script>
That works fine, except the element only grows downwards. What I'm trying to do is have the element grow vertically in both directions (in this case 50px in each direction) so it stays centered. Is there a way that it can be done?
Live Demo
var growEl = $("#grow"),
curHeight = $("#grow").height(),
curTop = growEl.offset().top,
newHeight = 200,
newMargin = curTop -(newHeight -curHeight)/2;
if(newMargin < 0){
newMargin = 0;
}
$("#grow").animate({height:newHeight+"px", marginTop:newMargin + 'px'});
Formula for figuring out what to make the margin
NewTopMargin = CurrentMargin-(NewHeight-OldHeight)/2
Thanks #bobsoap for reminding me to use offset.top
margin-top: -50px, height: 50px;
That sort of thing (sorry its not code formatted) i'm sure you can insert it correctly.

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