How to detect if an element is sticky or not? - javascript

In my Vue application, I have a navigation drawer that contains a sticky header and content underneath it.
The content is quite long and I'd like to display a divider or a border between the header and the content.
I know a border-bottom on the "sticky-header" div would do it but I want to apply a bottom border to the header only when it becomes stuck (fixed at the top) not when the header is in the default form (relative position).
Below is my code-
<v-navigation-drawer
v-if="drawerNodeData"
class="nav-drawer"
width="408px"
dark
color=#212121
v-model="drawerNode"
absolute
temporary
>
<div class="node-drawer">
<div class="sticky_header">
<div class="close-actor-panel">
<span></span> <!-- To stick the close icon to the right -->
<v-btn icon size="0.8em" #click.stop="drawerNode = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
<v-list-item class="title-and-nb-of-news">
<span v-if="this.drawerNodeData" class="node-title">{{
this.drawerNodeData.id.replaceAll("_", " ")
}}</span>
<div class="chip-for-news">
<DrawerNewsModale
:actorName.sync="actorName"
:filteredNews.sync="filteredNews"
></DrawerNewsModale>
</div>
</v-list-item>
</div>
<div class="content"></div>
</div>
</v-navigation-drawer>
Any idea how to detect if element is sticky or not so I can toggle the border accordingly?

The IntersectionObserver interface of the Intersection Observer API
provides a way to asynchronously observe changes in the intersection
of a target element with an ancestor element or with a top-level
document's viewport.
So, A few lines of IntersectionObserver in JavaScript and tricky usage of top: -1px in the CSS could help to achieve this.
The Below code is containing a header and some content. When the page will be scrolled and the header will be stuck at the top, a bottom border will be applied to the header and when the header is not sticky anymore, the border class will be removed.
const el = document.querySelector(".sticky-element")
const observer = new IntersectionObserver(
([e]) => e.target.classList.toggle("threshold-reached", e.intersectionRatio < 1),
{ threshold: [1] }
);
observer.observe(el);
#parent {
height: 2000px;
}
.sticky-element {
position: sticky;
top: -1px;
z-index: 1;
background: yellow;
}
/* styles for when the header is in sticky mode */
.sticky-element.threshold-reached {
border-bottom: 1px solid black;
}
<div id="parent">
<br />
<div class="sticky-element">Header</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
</div>
Important- Read more about how this intersection API works.

Related

How to change fixed button to change BG color if its scrolled over sections that has same color?

Basically, i have fixed button on bottom that scrolls over the page on mobile. The color of button is yellow and i want when the button scrolls over sections that are same color as button, to get additional class or change style directly inline and set BG color to white.
Is it possible with Observer or something similar?
Thanks!
The trouble with trying to use the Intersection Observer API in this case is twofold:
The yellow sections are not ancestors of the button, they're likely siblings.
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element...
The button is position: fixed, which doesn't play nicely with the internals of the API: Intersection observer does not work with target with position: fixed.
The old-school way of doing this would be to check the bounding box of the button against the bounding boxes of the yellow sections each time the page is scrolled.
That means calling Element.getBoundingClientRect() once for the button (it's bounding box should never change because it's position: fixed relative to the viewport) and once for each yellow section each time the scroll event is raised.
Here's a sample showing this approach:
const button = document.getElementById('some-action');
const buttonRect = button.getBoundingClientRect();
const yellowDivs = document.querySelectorAll('div.yellow');
const areIntersecting = (bounds1, bounds2) =>
bounds1.top < bounds2.bottom && bounds1.bottom > bounds2.top;
document.addEventListener('scroll', () => {
/* Use for...of, not .forEach so we can
return early. */
for (let item of yellowDivs) {
const itemRect = item.getBoundingClientRect();
if (areIntersecting(itemRect, buttonRect)) {
button.classList.add('white');
button.classList.remove('yellow');
/* We don't care how many yellow divs the button
is intersecting. Once we've found one, we can
return so we're not computing the rectangles
of the rest. */
return;
}
/* If none of the yellow divs were interecting,
reset the color of the button. */
button.classList.add('yellow');
button.classList.remove('white');
}
});
div.blue, div.white, div.yellow { height: 250px; }
.blue { background-color: blue; }
.white { background-color: white; }
.yellow { background-color: yellow; }
#some-action {
position: fixed;
top: 20px;
right: 20px;
padding: 10px 20px;
}
<div class="blue"></div>
<div class="yellow"></div>
<div class="white"></div>
<div class="blue"></div>
<div class="yellow"></div>
<div class="white"></div>
<div class="blue"></div>
<div class="yellow"></div>
<div class="white"></div>
<div class="blue"></div>
<div class="yellow"></div>
<div class="white"></div>
<div class="blue"></div>
<div class="yellow"></div>
<div class="white"></div>
<button id="some-action" class="yellow">Some Action</button>

How do I make a sticky footer in Next.js (React)

I want to make a sticky footer that will stick to the bottom if there's not enough content to fill up the whole page. I've searched up ways to do it in CSS, but a lot of them doesn't translate to React/Next since it involves messing with the html and body tag. I'm wondering if there are other ways to do it.
Here is the JSX for my Footer:
<div>
<footer>
<a href={"https://www.instagram.com/linghandmade18/"}>
<i className="fab fa-instagram" />
</a>
</footer>
<h2>Some Text</h2>
</div>
Here is my Layout file for Next.js:
const Layout = (props) => {
return (
<div>
<Navbar />
{props.children}
<Footer />
</div>
);
};
If you don't want to mess with html and body tags then you need a container on which you can apply your style. So first of all create a common container inside your Layout (add container class to parent element), like this:
const Layout = (props) => {
return (
<div class="container">
<Navbar />
{props.children}
<Footer />
</div>
);
};
Now you have a .container class to the div which is parent div of Navbar, Content and Footer. Now add following styles to the container class:
.container {
min-height: 100vh;
position: relative;
}
Because of this your container height will stay at least 100vh (viewport height), it will grow more if content length increases.
And for your footer component, make these changes if the h2 is part of the footer (for better accessibility).
const Footer = (
<footer>
<a href={"https://www.instagram.com/linghandmade18/"}>
<i className="fab fa-instagram" />
</a>
<h2>Some Text</h2>
</footer>
);
For footer styling you can add this style:
footer {
position: absolute;
bottom: 0;
left: 0;
}
This way it will always stay in the bottom position regardless of the content height.

How do I make a dynamic sticky toolbar without weird scroll glitch when page is slightly longer than page height?

I have the following code to move a toolbar that is below a header to stick to the top of the viewport when scrolling down. The problem is when the height of the content is slightly higher than the viewport. When you attempt to scroll to the bottom, it bounces back up to just above the end of the content with a weird visual glitch. It probably has to do with the fact that when the 'sticky' class is added, it increases the height of the page, but I'm not sure how to remedy it.
Javascript/JQuery:
var enableSticky = () => {
let headerHeight = $('.toolbar').offset().top;
$(window).scroll(() => {
if ($(window).scrollTop() > headerHeight) {
$('.toolbar').addClass('sticky');
} else {
$('.toolbar').removeClass('sticky');
}
});
};
CSS:
.toolbar {
width: 100%;
height: 84px;
position: relative;
}
.toolbar.sticky {
background-color: rgba(255,255,255,0.9);;
position: fixed;
top: 0;
z-index: 5;
margin-bottom: -84px;
}
HTML:
<div id="bcmMain" style="display: block;">
<h1 id="event-title"><span class="view-table-value" data-field-name="mopId">927</span> - <span class="view-table-value" data-field-name="subject">Sample Subject</span></h1>
<div class="toolbar">
<div class="status-toolbar-group toolbar-group">
<a class="button" id="level-button">Level <span class="view-table-value" data-field-name="level">3</span></a>
</div>
<div class="toolbar-group">
<a class="button" id="status-button">Status: <span class="view-table-value" data-field-name="status">complete</span></a>
<a class="button" id="pending-button">Mark Pending</a>
</div>
<div class="edit-toolbar-group toolbar-group">
<a class="button" id="submit-changes-button">Submit Changes</a>
<a class="button" id="cancel-changes-button">Cancel Changes</a>
</div>
<div class="toolbar-group">
<a class="button" id="edit-button">Edit</a>
<a class="button" id="clone-button">Clone</a>
</div>
</div>
<div id="edit-details">
<!-- CONTENT HERE -->
</div>
</div>
I've tried doing if ($(window).scrollTop() > headerHeight + 100) to see if allowing further scrolling before the it adds the sticky class, but it just changes the height at which the window is at when the glitch occurs.
What can I do to improve the code to get rid of this glitch?
EDIT: Added HTML. I tried to include just the relevant HTML, but let me know if anything else is needed. Thanks!
NOTE 1: This is embedded in a Confluence page. Confluence page header is hidden, but not the Confluence website header or the sidebar. See the below images to see what I'm talking about. Both images are at the top of the viewport at different scroll points.
Image of Not Sticky
Image of Sticky
NOTE 2: Not sure if this is important... data is pulled from a database via API to fill the fields on this page.
Not entirely sure I get what you mean, but I think you're referring to the 'bounce' of the content caused by removing the toolbar from the flow of the page.
The content always bounces the distance of the height of the toolbar regardless of the height of the content in relation to the viewport, but maybe it's more apparent then because you see the bottom of the content bounce as well as the top.
Anyway, if that is what you're referring to, I think this is your fix:
.toolbar.sticky {
position: fixed;
top: 0;
background-color: rgba(255,255,255,0.9);
}
.toolbar.sticky + #edit-details {padding-top:84px;}
I removed z-index:5; and margin-bottom:-84px; because they weren't doing anything.
Instead I added the .toolbar.sticky + #edit-details {padding-top:84px;} rule.
What this does is, it adds padding-top to the #edit-details that directly follows the .toolbar.sticky (that's what the + is for, read about it here). In other words; the padding-top of the content is dependent on the toolbar having the sticky class.
So when the .sticky class is added to the toolbar - and thereby 84px is removed from the flow of the page (which caused the 'bounce') - that same 84px is added again just before the content in the form of padding, causing the content to stay at exactly the same place.
When the .sticky class is removed again, the padding is removed again too.
- The padding-top should have the same value as the toolbar height.
I also removed position:relative; from .toolbar, because again, it wasn't doing anything.
You can test it below in the code snippet, though it may be easier to test in the jsfiddle, because there you can adjust the height of the viewport.
$(document).ready(function() {
let headerHeight = $('.toolbar').offset().top;
$(window).scroll(function() {
if ($(window).scrollTop() > headerHeight) {
$('.toolbar').addClass('sticky');
} else {
$('.toolbar').removeClass('sticky');
}
});
});
.toolbar {
width: 100%;
height: 25px;
border-bottom: 2px solid blue;
}
.toolbar.sticky {
position: fixed;
top: 0;
background-color: rgba(255,255,255,0.9);
}
.toolbar.sticky + #edit-details {padding-top:25px;}
/*ONLY FOR VISUAL STYLING*/
.toolbar-group {margin-right:5px; float:left;}
.toolbar-group a {float:left; padding:1px;}
.toolbar-group .button {background:lightblue;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bcmMain" style="display: block;">
<h1 id="event-title"><span class="view-table-value" data-field-name="mopId">927</span> - <span class="view-table-value" data-field-name="subject">Sample Subject</span></h1>
<div class="toolbar">
<div class="status-toolbar-group toolbar-group">
<a class="button" id="level-button">Level <span class="view-table-value" data-field-name="level">3</span></a>
</div>
<div class="toolbar-group">
<a class="button" id="status-button">Status: <span class="view-table-value" data-field-name="status">complete</span>|</a>
<a class="button" id="pending-button">Mark Pending</a>
</div>
<div class="edit-toolbar-group toolbar-group">
<a class="button" id="submit-changes-button">Submit|</a>
<a class="button" id="cancel-changes-button">Cancel</a>
</div>
<div class="toolbar-group">
<a class="button" id="edit-button">Edit|</a>
<a class="button" id="clone-button">Clone</a>
</div>
</div>
<div id="edit-details">
content - first<br>content - second<br>content - third<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>content - forelast<br>content - last<br>
</div>
</div>
jsfiddle: https://jsfiddle.net/nd9etLjy/1/
I added some CSS to style the toolbar, this is not important for functionality.
I also changed your arrow-functions to regular ones, because I couldn't get the arrow-functions to work, but that's most likely due to my inexperience with them.

Interactions with Background being blocked by Div over the background

I am using Particles.js on the background. I have div layer over the canvas which contains text and images.
This div blocks the interactions with the background killing the experience. Is there a way to make sure the div allows the interactions through it with the background. Above and below the Div the interactions work well as the content is vertically centered.
Demo Link
I see this css in your code. ( line no. 19)
#hero-unit .particles-js-canvas-el {
z-index: -1 !important;
}
Change it to
#hero-unit .particles-js-canvas-el {
z-index: 1 !important;
}
and
#hero-unit #hero-wrapper {
z-index: 1!important;
}
FIX to make the the interaction through the text or next to it
wrap the divs in seperate .col-md-9 rows
<div class="row">
<!-- first row -->
<div class="col-md-9">
<img class="hero-logo hidden-xs" src="http://creo.mink7.com/wp-content/themes/creo/images/logo-home.png" alt="">
<h1 class="head">Together we can Make a universe of smart innovations for a Better tomorrow</h1>
</div>
<!-- second row -->
<div class="col-md-9 newsletter">
<div id="frontpage-mailchimp">
<h4>Join the Community of Makers</h4>
<!-- MailChimp for WordPress v3.0.10 - https://wordpress.org/plugins/mailchimp-for-wp/ -->
<form id="mc4wp-form-1" class="mc4wp-form mc4wp-form-114" method="post" data-id="114" data-name="Join us in making things better">
<!-- mailchimp stuffs -->
</form>
</div>
</div>
</div>
and finally, this css
#hero-wrapper .col-md-9{
z-index: 1; // reduce the default z-index for div
}
#hero-wrapper .col-md-9.newsletter{
z-index: 9999; // increase the z-index for newletter div only
}

How to add `scroll` bar to a `div`, when its height as `auto`

In my container, there is multiple childrens, one of the 'div' getting appended by content in that.
when the total height of the container(parent) overflows, i would like to add the scroll bar to the div
is it possible to do by css?
here is the html :
<div id="container">
<div>
<h2>Heading</h2>
</div>
<div class="content">
</div>
<div class="footer">
Footer
</div>
</div>
<button>Add</button>
Js :
var p= "</p>Some testing text</p>";
$('button').click(function(){
$('.content').append(p);
});
jsfiddle
UPDATE
I don't want to put the over-flow to container, if so my footer will hide. i require my user need to see the add button always. I can't put my button out side of the container again there would be multiple content in to the container
UPDATE
I find a solution by js is it possible to made without using `js'?
jsSolution
Yes, it is possible to do in CSS. Simply add this CSS rule to #container:
overflow-y:scroll;
Alternatively add this to show the scroll bar only when necessary:
overflow-y:auto;
http://jsfiddle.net/doesfmnm/2/
var p= "</p>Some testing text</p>";
$('button').click(function(){
$('.content').append(p);
});
.content{
border:1px solid red;
height:300px;
width:200px;
overflow-y:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div>
<h2>Heading</h2>
</div>
<div class="content">
</div>
<div class="footer">
Footer
</div>
</div>
<button>Add</button>
Adding a little more explanation to what #Guy3000 said. You're appending (adding after) into an element with the class 'content'. Let's consider what that means for the parent .container class. By adding content into a div inside of the parent, your parent will need to either grow to compensate for the added content, or it will need to have a y-axis scroll that permits content longer than the height of the container.
This means you can approach the dilemma you're facing by adding height to the container element, or you can keep a fixed height on the container and have a frame with a y-axis scroll bar contain the added content.
Here is the solution i find :
<div id="container">
<div id="up">Text<br />Text<br />Text<br /></div>
<div id="down">
<div class="content"></div>
</div>
<div class="misc"><button>Add</button></div>
</div>
css :
#container { width: 300px; height: 300px; border:1px solid red;display:table;}
#up { background: green;display:table-row;height:0; }
#down { background:pink;display:table-row; overflow-y:auto}
.misc {
display:table-row;
background:gray;
height:30px;
}
.content {
overflow:auto;
height:100%;
}
Live
js solution :
http://jsfiddle.net/doesfmnm/4/

Categories