Javascript scrollIntoView only in immediate parent - javascript

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>
}

Related

How to make an invisible clickable area that would toggle scrolling (JavaScript)?

I'm making a small website built entirely with flexbox columns and rows. One of the rows is horizontally scrollable — howewer, I want it to be scrolled not only with scrolling, but also by clicking either on left or right half of the visible part of the row. Plus, hovering on the left side should change cursor to cursor: w-resize and on the left side — to cursor: e-resize.
see screenshot
see a sketch of what i'm trying to achieve
<div class="project">
<div class="project-images">
<div id="project-image"><img id="image-standart-height" style="max-height: 400px" src="/Users/andrei/Desktop/All Work/portfolio and cv/my website/images/bahorvesna.png" alt=""></div>
<div id="project-image"><img id="image-standart-height" style="max-height: 400px" src="/Users/andrei/Desktop/All Work/portfolio and cv/my website/images/bahorvesna.png" alt=""></div>
<div id="project-image"><img id="image-standart-height" style="max-height: 400px" src="/Users/andrei/Desktop/All Work/portfolio and cv/my website/images/bahorvesna.png" alt=""></div>
<div id="project-image"><img id="image-standart-height" style="max-height: 400px" src="/Users/andrei/Desktop/All Work/portfolio and cv/my website/images/bahorvesna.png" alt=""></div>
</div>
<div class="project-text">
Bahor/Vesna
</div>
</div>
#project-image {
flex: 0 0 auto;
max-height: 400px;
}
#image-standart-height {
max-height: 400px;
}
.project-text {
display: flex;
gap: 20px;
align-items: baseline;
}
I was thinking of creating an invisible layer on a different z-axis level, but I assume it would be difficult not to ruin the flexbox.
Is it possible to make a function in JS to detect when a mouse is entering a certain area of the visible part of the div, change cursor and make the area clickable?
You can also do it without adding any extra div's, by checking the position of the mouse pointer relative to the container. Try it out by clicking the left and right half of the box in the example.
If you have multiple elements, all you need to do is loop the set of elements and put the two event listeners inside the loop.
const containers = document.querySelectorAll('.container');
containers.forEach((container) => {
container.addEventListener('click', (e) => {
const clientRect = e.currentTarget.getBoundingClientRect();
if ((e.pageX - clientRect.left) < clientRect.width / 2)
console.log('Clicked left half');
else
console.log('Clicked right half');
});
container.addEventListener('mousemove', (e) => {
const clientRect = e.currentTarget.getBoundingClientRect();
if ((e.pageX - clientRect.left) < clientRect.width / 2)
container.style.cursor = 'w-resize';
else
container.style.cursor = 'e-resize';
});
});
.container {
width: 300px;
height: 100px;
border: 1px solid #000;
margin: 50px auto;
}
<div class="container"></div>
<div class="container"></div>
<div class="container"></div>
<div class="container"></div>
<div class="container"></div>

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)

How to scroll MULTIPLE scrollbars at the same time

When I scroll the wrapper div the 2 images should also scroll together with it but it seems only the 2 images sync together but not the wrapper
The 2 images should sync with the body's scrollbar
$(function(){
$('.linked').scroll(function(){
$('.linked').scrollTop($(this).scrollTop());
})
})
#left { width: 300px; height: 400px; overflow: scroll; float: left; }
#right { width: 300px; height: 400px; overflow: scroll; float: left; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<div id="left" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>
<div id="right" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>
This might help
let l = document.getElementById(".left");
let r = document.getElementById(".right");
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});
If I understood correctly you're question is how to sync a set of scroll bars including the window scroll bar? This is possible and I will demonstrate for education purposes only however this is not something I would personally recommend.
Manipulating the browser window scroll bar can create a poor user experience as it interferes with the expected behavior of the scroll bar. Additionally, scrollbar manipulation can cause unexpected behavior and may not work consistently across different browsers and devices, leading to a fragmented user experience.
This example does not take into consideration the dimensions of the browser window and elements with scroll bars so be warned that it might not work as expected.
// Pseudo code
// 1. Query the scroll bars you want to sync
// 2. Add event listener to each of them
// 3. In the event listener, set the scroll position of the other scroll bar(s) to the same value
// Step 1
const scrollBarContainers = [
...document.querySelectorAll('.scrollable'),
document // Include document for window scrolling
];
// Step 2
scrollBarContainers.forEach(container => {
container.addEventListener('scroll', syncScrolls);
});
// Step 3
function syncScrolls(e) {
scrollBarContainers.forEach(container => {
if (container !== e.target) {
// The document element doesn't directly have a scrollLeft property.
// It's on the scrollingElement. The other elements have it directly.
if (container.scrollingElement) {
container.scrollingElement.scrollLeft = e.target.scrollLeft;
} else if (e.target.scrollingElement) {
container.scrollLeft = e.target.scrollingElement.scrollLeft;
} else {
container.scrollLeft = e.target.scrollLeft;
}
}
});
}
.container {
height: 100px;
border: 1px solid gray;
width: 900px;
margin-bottom: 1em;
overflow: auto;
}
span {
display: inline-block;
width: 1200px;
height: 50px;
background-color: red;
}
<div class="container scrollable"><span></span></div>
<div class="container scrollable"><span></span></div>

Detect is child container at the bottom of the parent in jQuery

I have HTML like this:
<div class="cont">
<!-- some elements -->
<div class="child fixed">Child</div>
</div>
child is with position fixed (class fixed). Inside cont there are another elements, which make it with higher height than child.
I have scroll event on document:
$(document).scroll(function(e) { ... }
I want when some1 scroll and child is at the bottom of cont to remove fixed class.
How can I detect on scroll (document scroll) that some element is at the bottom of some parent element (I mean when bottom of the child is in the same position as its parent cont.) ?
Edit
#devlincarnate this is not "how to check is it last child" question.
Sometimes you just don't need JS. This is what I'd do, using CSS position: sticky - if I got your question (and problem) right...
* { margin: 0;box-sizing: border-box; }
body { border: 10px dashed #000; }
#footer { background: #0fb; height: 150vh;}
#cont { background: #0bf; height: 200vh;}
#child { background: #f0b; height: 20vh;
position: sticky;
top: 0;
}
<div id="cont">
<div id="child">CHILD FIXED.... and magical</div>
CONTENT...
</div>
<div id="footer">FOOTER</div>

Margin issue with jquery load()

I am loading html page inside a div with jquery. It does work fine.
var loginBtn = $("#loginBtn");
var loginPage = $("#login");
var submitBtn = $("#submitBtn");
var submitPage = $("#submit");
var checkBtn = $("#checkBtn");
var checkPage = $("#check");
loginPage.load( "login.html" );
submitPage.load( "submitPoints.html" );
checkPage.load( "checkPoints.html" );
body {
margin: 0 !important;
padding: 0 !important;
background-color: white;
}
#mainFrame {
width: 100%;
height: 300px;
background-color:cadetblue;
padding-top: 0;
margin-top: 0px;
position: relative;
}
<div id="mainFrame">
<div id="login"></div>
<div id="check"></div>
<div id="submit"></div>
</div>
My issue is that if the loaded html has no content, the margin between the parent document body (white) and the top of the loaded html (green) is none (that's what I want, it's ok).
However as soon as I add content to the loaded html, a gap is generated at the top of the page :\
I thought it was all about setting some line-height prop in the css but it seems helpless.
Any ideas what I am doing wrong ?
What you are seeing is the top margin of the first piece of content overflowing its container (also known more commonly as margin collapsing):
body {
background:yellow;
}
#container {
background:green;
height:300px;
}
<div id="container">
<h1>I have a top margin of 1em by default that is overflowing into the body.</h1>
</div>
If you give your container element a padding of that same amount, the margin space of the body won't be used and the element will be pushed down in the green area.
body {
background:yellow;
}
#container {
background:green;
height:300px;
padding:1em;
}
<div id="container">
<h1>I have a top margin of 1em by default that is now contained within my parent.</h1>
</div>
Or, you could set the top margin of the first piece of content to zero:
body {
background:yellow;
}
#container {
background:green;
height:300px;
}
#container > h1:first-child { margin-top:0; }
<div id="container">
<h1>My top margin has been set to zero.</h1>
</div>
Finally, you could set the overflow of the content area to auto but (although this seems to be the popular answer), I don't prefer this approach as you run the risk of unintended fitting of the content as the content changes and/or the container size changes. You give up a bit of sizing control:
body {
background:yellow;
}
#container {
background:green;
height:300px;
overflow:auto;
}
<div id="container">
<h1>The content area has had its overflow set to auto.</h1>
</div>
When you load new content it gets rendered in the document and those new elements might have properties. In this case, most probably the Login has a margin value. Another option is that it has a class or some selector that is being picked up by a CSS file which appends the margin to it.
Easiet way would be to right-click on the Login element, choose inspect, and analyze the style of the element with web-dev / style.
If you want to keep the margin on the inner content, you should set an overflow. Look what happens when we remove the overflow: auto line from .content > div (try clicking the box after running the code sample below).
This is because of margin collapsing. The margin on the inner content is combined with the margin on the outer element and applied on the outer element, i.e. two margins of the two elements are collapsed into a single margin.
document.querySelector('.content').addEventListener('click', (e) => {
e.target.classList.toggle('overflow');
});
html,
body {
margin: 0;
padding: 0;
}
.outer {
width: 200px;
background: red;
}
.content > div {
width: 100%;
height: 300px;
background: cadetblue;
cursor: pointer;
}
.content > div.overflow {
overflow: auto;
}
.test {
margin: 10px;
display: block;
}
<div class="outer">
<div class="content">
<div><span class="test">Test</span></div>
</div>
</div>

Categories