Anchor to overflow auto (scrollable) content - javascript

I am building a comment system, I want the user to be able to be redirected directly to the comment (just like stack overflow comment notification) from anywhere else.
Here's what I got so far. (the demo below doesn't display well, please refer to codepen)
const comment_box = document.getElementById('comment-box');
const fourth = document.getElementById('btn4');
const seventh = document.getElementById('btn7');
const tenth = document.getElementById('btn10');
function comment_jump(id){
var el = document.getElementById(id);
// getBoundingClientRect: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
// top (of this comment refer to parent) = top (of this comment refer to page) - top (of container refer to page)
var tp = el.getBoundingClientRect().top - comment_box.offsetTop;
comment_box.scrollTo(0, tp);
}
fourth.addEventListener('click', ()=>{
comment_jump('comment4');
});
seventh.addEventListener('click', ()=>{
comment_jump('comment7');
});
tenth.addEventListener('click', ()=>{
comment_jump('comment10');
});
body{
background: lightgrey;
}
#comment-box{
width: 500px;
height: 300px;
background: grey;
overflow: auto;
}
.comment{
width: 100%;
height: 33.3%;
border: 1px solid;
font-size: 2em;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
button{
font-size: 1.5em;
margin: 2em;
}
button:hover{
opacity: .5;
cursor: pointer;
}
<div id="comment-box">
<div class="comment"></div>
<div class="comment"></div>
<div class="comment"></div>
<div id="comment4" class="comment">4th</div>
<div class="comment"></div>
<div class="comment"></div>
<div id="comment7" class="comment">7th</div>
<div class="comment"></div>
<div class="comment"></div>
<div id="comment10" class="comment">10th</div>
<div class="comment"></div>
<div class="comment"></div>
</div>
<button type="button" id="btn4">4th</button>
<button type="button" id="btn7">7th</button>
<button type="button" id="btn10">10th</button>
The expected output is that when I click 4th, 7th, 10th, it jump to corresponding area.
I will be so glad if someone suggest which part I went wrong.

Might I suggest trying out scrollIntoView as a possible solution?
function comment_jump(id){
var el = document.getElementById(id);
el.scrollIntoView();
}
From Mozilla's docs:
scrollIntoView() method scrolls the element's parent container such that the element on which scrollIntoView() is called is visible to the user
So it will scroll the comment container for you to the desired element.

Related

css scroll-snap: focusing on the element which got snapped to

I implemented a horizontal grid with some cards in them. The grid uses CSS scroll-snap and it works nicely when navigating with mouse/touchscreen.
The problem occurs when the grid is navigated using a keyboard. Pressing tab after navigating through the grid with arrow keys causes the view to jump back to the element that got the focus, not the card which is current snapped to.
My ideal behaviour when pressing tab is, to focus on the card which is currently snapped to.
Any suggestions to make this possible?
As far as I can tell there is currently no way to handle this natively. Nils Schwebel's answer is not going to be very elegant, but it looks like the best way to go.
Here's a working example:
Note: I've added quite a bit of pure decoration to make it easier to understand, so you may need to pick out the relevant parts after some testing.
const main = document.getElementById("Main"),
sections = document.getElementsByClassName("section");
main.addEventListener('scroll', (e) => {
// Grab the position yo are scrolled to (the top of the viewport)
let pos = main.scrollTop;
for (let i = 0, l = sections.length; i < l; i++) {
// Since our stap-align is centered, get the position of the middle of the viewport relative to the current section's top (if your snap items are not full-height, it might require using half the viewport's height instead)
let relativePos = sections[i].offsetTop - pos + (sections[i].offsetHeight / 2);
// Check if the point we found falls within the section
if (relativePos >= 0 && relativePos < sections[i].offsetHeight) {
sections[i].focus();
break;
}
}
});
body {
margin: unset;
}
main {
display: flex;
flex-wrap: wrap;
align-items: stretch;
justify-content: center;
width: 100%;
height: 100vh;
background-color: #222;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
section {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
scroll-snap-align: center;
}
section:focus {
border: none;
outline: none;
}
#s1 {
background-color: #d72748;
}
#s2 {
background-color: #b51f7e;
}
#s3 {
background-color: #e64869;
}
#s4 {
background-color: #e79946;
}
section h2 {
color: white;
}
<main id="Main">
<section class="section" id="s1" tabindex="1" aria-labelledby="a1">
<h2 id="a1">AREA 1</h2>
</section>
<section class="section" id="s2" tabindex="1" aria-labelledby="a3">
<h2 id="a2">AREA 2</h2>
</section>
<section class="section" id="s3" tabindex="1" aria-labelledby="a2">
<h2 id="a3">AREA 3</h2>
</section>
<section class="section" id="s4" tabindex="1" aria-labelledby="a4">
<h2 id="a4">AREA 4</h2>
</section>
</main>
I would add a scroll listener and just check if the element is at the top of the scroll view. You may be able to modify one of these solutions: How to check if element is visible after scrolling?

Beginner - I want the scroll bar to move down with JS

Following guys, I used the code:
window.scrollBy (0, window.innerHeight)
And yes, it scrolls down the instagram home page.
However, when I go to my story, and see who viewed it, I want him to scroll the story's NOT scroll bar on the home page.
When I open the console and use the code I mentioned above, it returns me undefined and does not do what I want.
How do I scroll the bar of people who have viewed my story? Not from the home page (complete)?
EDIT
enter image description here
var button = document.querySelector('#scroll-child')
button.addEventListener('click', function() {
var childBlock = document.querySelector('.child')
childBlock.scroll(0, 50)
})
.parent {
height: 300px;
background-color: yellow;
text-align: center;
overflow: scroll;
}
.child {
width: 300px;
height: 200px;
background-color: crimson;
text-align: center;
overflow: scroll;
}
.child-content {
margin: 20px;
height: 1000px;
}
<div class="parent">
<button id="scroll-child">
scroll child content!
</button>
<div class="child">
<div class="child-content">
something1
something2
something3
something4
something5
something6
something7
something8
</div>
</div>
</div>
find your story block with its css selector:
var storyBlock = document.querySelector('#my-story-block-id')
scroll inside block
storyBlock.scroll(0, storyBlock.innerHeight)

Javascript scrollIntoView only in immediate parent

How do I make scrollIntoView only scroll the immediate parent (e.g. div with overflow-y: scroll;) and not the whole page?
I have a web interface I'm making for an internal, very-specific purpose. Among other elements on my page is a div with a specified height and overflow-y is scroll.
I have data which will periodically appear in this area and I want it to always be scrolled to the bottom (e.g. the console output of a subprocess on some remote server).
If I use scrollIntoView, it scrolls the overflow-y div..... but also scrolls the whole page.
On a computer with a large monitor, this isn't an issue, but on my laptop with a smaller screen it also scrolls the whole window, which is definitely not the intended/desired behavior.
I think what you might be looking for is
.scrollIntoView({block: "nearest", inline: "nearest"});
Where supported (basically anywhere except IE and SafarIE) this will do the 'least' movement to show the element; so if the outer container is visible, but the target element is hidden inside that container -- then it should scroll the inner container but not the page.
I think you're looking for a combination of scrollTop and scrollHeight. You can use the first to set where you want the div to scroll to, and the second to get the height of all the info in the div:
var scrollyDiv = document.getElementById("container");
scrollyDiv.scrollTop = scrollyDiv.scrollHeight
setInterval(() => {
var textnode = document.createElement("P");
textnode.innerHTML = "Whatever"
scrollyDiv.appendChild(textnode);
scrollyDiv.scrollTop = scrollyDiv.scrollHeight
}, 1000)
#container {
height: 200px;
overflow-y: scroll;
}
#big-content {
height: 400px;
background-color: green;
}
<div id="container">
<div id="big-content"></div>
<p>The bottom</p>
</div>
I tried to reproduce your case and I think that scrollIntoView() will not work as you wish. Try to use scrollTop instead.
Hope it will save your time.
const btn = document.getElementById('js-scroll');
btn.addEventListener('click', () => {
const targets = document.querySelectorAll('.scrollable');
targets.forEach(t => {
// Scroll each item
t.scrollTop = t.scrollHeight;
});
});
.scroll-btn {
position:fixed;
left: 10px;
top: 10px;
padding: 10px;
font-weight: 700;
font-size: 16px;
}
.content {
min-height: 100vh;
background-color: #efefef;
padding: 35px 10px;
}
.scrollable {
margin: 15px; 5px;
padding: 5px;
background-color: #fafafa;
height: 500px;
overflow-y: scroll;
overflow-x: hidden;
}
.scrollable-data {
padding: 10px;
margin-bottom: 1px;
min-height: 600px;
background-color: #55b7ab;
}
<button id="js-scroll" class="scroll-btn">Scroll</button>
<section class="content">
<div class="scrollable">
<div class="scrollable-data">Some data 1</div>
<div class="scrollable-data">Some data 1</div>
</div>
<div class="scrollable">
<div class="scrollable-data">Some data 2</div>
<div class="scrollable-data">Some data 2</div>
</div>
<div class="scrollable">
<div class="scrollable-data">Some data 3</div>
<div class="scrollable-data">Some data 3</div>
</div>
</section>
Solution for React 16
Here is an answer for this problem using React 16, for people who want to have a scrollable element scroll to its bottom upon some event. I was having the problem that scrollIntoView() was causing the whole window to adjust, which was undesirable; just need the scrolling element to scroll to the bottom (like a terminal) on demand.
Add a ref to the element with overflowY: "scroll", and use the formula this.body.current.scrollTo(0, this.body.current.scrollHeight) like so:
constructor() {
...
this.body = React.createRef() // Create a ref to your scrollable element
}
...
componentDidUpdate(prevProps, prevState) { // or whatever action you want
// The Important Part, where you scroll to the y-coord
// that is the total height, aka the bottom.
// .current is important, as you want the version of
// that ref that is rendered right now.
this.body.current.scrollTo(0, this.body.current.scrollHeight)
...
}
...
render() {
return (
<div style={style.container}>
<div ref={this.body} style={style.body}> // React ref tagged here
....
</div>
</div>
}

Scroll smoothly by 100px horizontally

Heyjo,
problem: I am looking for a javascript or jQuery code since a week to get an implemented scrollbutton on my website working. The moment I fail is when the button should work multiple times: his task is not so scroll to a dedicated element, it should scroll left by, for instance, 100px. Furthermore the scrolling is supposed to happen smoothly (in other words, animated) in a proper section.
what I tried: til now I tried to fulfill this task with $('#idofsection').animate({scrollLeft: 100}, 800) but obviously it didn't work. The Problem was, one couldn't use it multiple times, it just scrolled to a position in my section. Afterwards I used javascript's scrollBy(100, 0) or scrollLeft += 100px, but unfortunately didn't got it to scroll smoothly.
I hope someone can help me because I spent so much time on this issue without finding a solution. Thanks a lot, Sven
You can use scrollBy(100, 0) just like you tried and add this css property to the viewport where you want to scroll:
scroll-behavior: smooth;
.window{
width: 200px;
height: 100px;
border: 1px red solid;
overflow: hidden;
scroll-behavior: smooth;
}
.container{
width: 1000px;
height: 200px;
}
.buttons{
width: 200px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
<div id="window" class="window">
<div class="container">
fjsdlf jslkd flsakj flksad jflkjsa dlfj slakd jflskad flksdaj lfk sadlkfj asldk fslkad fjlkasd flksa jdlf jsadlkfj slkda jflksadj flksa jdlkfj sadlk jflksadj flksjadflksadj flksdaj flksdaj flksdaflksjdflk sjdalkfj skdal fjlksadj flksa fklsjadfklj sadklfj salkdjf lksadj flksjad lfkj sadlkf jslakdjf lksdaj flkasj flkjsa dlfskal flsa jdas lkfjskad fj
</div>
</div>
<div class="buttons">
<button onclick="document.getElementById('window').scrollBy(-100,0)">
<-
</button>
<button onclick="document.getElementById('window').scrollBy(100,0)">
->
</button>
</div>
Solution also here: JSFiddle
So use the animation properties += to adjust it from current position.
$("#next").click(function(){
$('#foo').stop().animate({scrollLeft: "+=100"}, 800);
return false;
});
div {
width: 200px;
overflow: auto;
padding: 1em;
border: 1px solid black;
}
div p {
width: 1000px;
border: 2px dashed #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="foo">
<p>TEST</p>
</div>
<button id="next">Next</button>

Scroll to bottom of hidden div?

I have a simple chat JS application, with a div.chat-holder holding all chat messages within a pane on the overall window. I set height of '.chat-holder so it remains fixed in size, and allows for scrolling of all the messages.
<style>
.chat-holder {
height: 30px;
overflow-y: scroll;
}
</style>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">
first msg
</div>
<div class="chat-item">
second msg
</div>
....
<div class="chat-item">
last msg
</div>
</div>
</div>
On page load, I scroll to the bottom by setting the scrollTop of the holder:
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
and this works fine.
Problem occurs when I start with div.pane set to display:none. Ideally, I look to have a separate event to "show/hide" the chat pane, and start with the pane hidden.
When the parent pane is hidden, the .chat-holder scrollHeight is 0, so on load, the hidden pane won't be scrolled to the bottom. Which means when the pane is displayed, the chats are not scrolled to the most recent chats. You can see this in the following snippet: with .pane initially not displayed, scroll isn't set. If you set .pane to start displayed, then scroll works fine.
Is there anyway to "scroll to the bottom" while parent is hidden? (Yes, I know I could do this by detecting when the chat-holder is exposed & then scroll to the bottom, but I'm looking to do it on load.)
$(function() {
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
$('button').click(function() {
$('.pane').toggleClass('active');
});
});
.chat-holder {
height: 30px;
overflow-y: scroll;
border: thin solid black;
}
.chat-item {
font-size: 20px;
}
.pane {
display: none;
}
.pane.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">first msg</div>
<div class="chat-item">second msg</div>
<div class="chat-item">last msg</div>
</div>
</div>
<button>Toggle pane</button>
You can get creative and use opacity or visibility rules instead of display: none:
$(function() {
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
$('button').click(function() {
$('.pane').toggleClass('active');
});
});
.chat-holder {
height: 30px;
overflow-y: scroll;
border: thin solid black;
}
.chat-item {
font-size: 20px;
}
.pane {
opacity: 0;
position: absolute;
z-index: 1;
}
.pane.active {
opacity: 1;
position: relative;
}
button {
z-index: 2;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">first msg</div>
<div class="chat-item">second msg</div>
<div class="chat-item">last msg</div>
</div>
</div>
<button>Toggle pane</button>

Categories