How to get the current Viewable elements in a page using javascript? - javascript

I'm making a nextjs app ,
I want to change the navbar active button according the the visible sections in the viewport
the page children look like this
<div id="section1" > </div>
<div id="section2" > </div>
...
<div id="faq" > </div>
<div id="contact" > </div>
for example when the user scrolls and the current div in the screen is the contact form
I set it the route to /#contact
My main problem is how to detect the current viewable element in the viewport
How can I achieve this ?

You can use Intersection Observer. Here is a live demo, where the list items on the header are automatically highlighted as you scroll over each section.
Javascript:
// simple function to use for callback in the intersection observer
const changeNav = (entries, observer) => {
entries.forEach((entry) => {
// verify the element is intersecting
if(entry.isIntersecting && entry.intersectionRatio >= 0.55) {
// remove old active class
document.querySelector('.active').classList.remove('active');
// get id of the intersecting section
var id = entry.target.getAttribute('id');
// find matching link & add appropriate class
var newLink = document.querySelector(`[href="#${id}"]`).classList.add('active');
}
});
}
// init the observer
const options = {
threshold: 0.55
}
const observer = new IntersectionObserver(changeNav, options);
// target the elements to be observed
const sections = document.querySelectorAll('section');
sections.forEach((section) => {
observer.observe(section);
});
HTML:
nav
ul
li
a(href='#one').active One
li
a(href='#two') Two
li
a(href='#three') Three
li
a(href='#four') Four
li
a(href='#five') Five
section#one
p Slide One
section#two
p Slide Two
section#three
p Slide Three
section#four
p Slide Four
section#five
p Slide Five
CSS:
nav
position fixed
width 100%
top 0
left 0
right 0
background-color white
ul
list-style-type none
display flex
align-items center
justify-content space-around
width 100%
max-width 800px
height 50px
margin 0 auto
padding 0
li
display inline-block
padding 5px
a
display block
height 40px
padding 0 20px
line-height 40px
text-decoration none
text-transform uppercase
color #323232
font-weight bold
border-radius 4px
transition background-color 0.3s ease-in
&:hover
&:active
&:focus
background-color rgba(#B8D6A8, 0.5)
&.active
background-color rgba(#B8D6A8, 0.5)
section
display flex
align-items center
justify-content center
min-height 100vh
p
text-align center
color white
font-size 3.5em
font-weight bold
text-transform uppercase
#one
background-color #6CA392
#two
background-color #FFA58C
#three
background-color #FF4F30
#four
background-color #576B51
#five
background-color #392A1B

Related

Setting `element.style.height` not working as expected if `element.style.box-sizing` is `border-box`

I am having an understanding problem with the following code:
let myDiv1 = document.getElementById("myDiv1");
alert("Click me to make 'Hello' vanish");
myDiv1.style.height = "0px";
let myDiv2 = document.getElementById("myDiv2");
alert("Click me to make 'World' vanish");
myDiv2.style.height = "0";
.myClass1 {
box-sizing: border-box;
overflow: hidden;
padding-top: 2em;
padding-bottom: 2em;
background-color: yellow;
}
.myClass2 {
box-sizing: content-box;
overflow: hidden;
padding-top: 2em;
padding-bottom: 2em;
background-color: orange;
}
<body>
<div id="myDiv1" class="myClass1">
Hello
</div>
<div id="myDiv2" class="myClass2">
World
</div>
</body>
I understand the behavior of the second (orange) div: It has box-sizing: content-box;, so its height does not include the padding or the borders. Hence, when its height is set to 0, it shrinks basically by the height of the text "World", but the padding is left as-is. Since the padding exceeds the original text height, the text is still visible in the padding. Only that part of padding which is now outside the div (due to the reduced height) becomes invisible (due to overflow: hidden;).
I do not understand the behavior of the first (yellow) div, though. It has box-sizing: border-box;, so its height does include the padding and the borders. Hence, when its height is set to 0, it should shrink to "real" zero height, meaning that the text, the padding and the borders then should be outside the div and thus should be invisible (due to overflow: hidden;).
Can anybody explain why this is not the case and why the first div behaves just like the second div?
P.S. Tested in Firefox and Chrome, both up-to-date (production channel) at the time of writing this.
border-box tells the browser to account for any border and padding in the values you specify for an element's width and height. If you set an element's width to 100 pixels, that 100 pixels will include any border or padding you added, and the content box will shrink to absorb that extra width. This typically makes it much easier to size elements. ref
Here is an example to better ilustrate your issue:
.box {
display: inline-block;
padding-top: 2em;
padding-bottom: 2em;
border: 2px solid blue;
background: linear-gradient(red, red) content-box, orange;
height: 100px;
animation:move 5s linear infinite alternate;
}
#keyframes move{
to {
height:0;
}
}
<div class="box">
World
</div>
<div class="box" style=" box-sizing: border-box;">
World
</div>
The first example is the trivial one where we decrease the height (red area) until 0 and the animation never stop.
In the second case the height include the padding and border so before reaching 0 the content area is already 0 that's why the animation seems to stop because we cannot decrease more than 0 and the border/padding already consumed all the space.
It's logical that when height is equal to 0 both are the same since in the first one we tell the browser that the content area should be 0 (we don't care about padding/border) and in the second case we told the browser to account for the padding/border into the height we specified so we have less room for the content area and since we cannot have less than 0 then its 0.
In other words, you aren't setting the total height that will be split between the content area, padding and border but you are setting a height and the browser is shrinking it as much as possible to include the border and padding.
Related for more examples and details: box-sizing: border-box with no declared height/width

Dynamically reset width of div using JavaScript?

I'm trying to animate a slide in menu using CSS/JavaScript (SASS/CoffeSript). It works like this, the only problem is that the width of the menu which slides in is hardcoded (233px). How can I dynamically get the width of the div and set it back, when I need to set the width of the div to 0 in CSS on start? Can I somehow calculate what the div width should be and set this again?
Any ideas how I might solve this to get the width dynamically, so that I can add/remove items to the menu without changing a hardcoded value in the CoffeScript?
HTML/PUG:
// Module: Header
.header
.header--logo
a(href="/", class="link-logo")
img(src="/img/logo.svg")
.header--menu#menu
a(href="#", class="link-menu")
img(src="/img/menu.svg", id="clicked-menu-item")
ul#nav
li: a(href="") projects
li: a(href="") contact
li: a(href="") self
CSS/SASS:
.header
position: sticky
top: 0
left: 0
background-color: $lightColor
display: flex
justify-content: space-between
padding: $baselineHeight
&--menu
display: flex
align-items: center
ul
width: 0px
overflow-x: hidden
transition: width ease-in-out 300ms
li
display: inline
margin-left: $baselineHeight
JavaScript/CoffeScript:
element = document.getElementById('clicked-menu-item')
element.onclick = (obj) ->
if (obj.srcElement.classList.contains('clicked'))
document.getElementById("nav").style.width = "0px"
else
document.getElementById("nav").style.width = "233px"
obj.srcElement.classList.toggle('clicked')
false

How to horizontally centre a stars rating image within a Bootstrap column?

I have a stars rating image which is controlled using Javascript and CSS to display a rating out of five (in quarter steps, twenty is the dimensions of the star in pixels):
$.fn.stars = function() {
return $(this).each(function() {
var val = Math.round(parseFloat($(this).html())*4)/4;
var size = Math.max(0, (Math.min(5, val)))*20;
var $span = $("<span />").width(size);
$(this).html($span);
});
}
$(function() {
$("#avg-rating").html('<span class="stars">{{ avg_rating }}</span>');
});
span.stars, span.stars span {
display: block;
background: url(stars.png) 0 -20px repeat-x;
width: 100px;
height: 20px;
}
span.stars span {
background-position: 0 0;
}
The image is displayed in a Bootstrap column:
<div class="container-fluid">
<div class="text-center">
<div class="row">
<div class="col-xs-6"><div id="avg-rating"></div></div>
<div class="col-xs-6">blah blah</div>
</div>
</div>
</div>
The image is currently left justified in its column. I have tried several things in CSS to align the image horizontally in its column, such as adjusting the display attribute or background position. But I am unable to move the image without upsetting the functionality of the image manipulation.
Update
Using inline-block instead of block messes up the placement of the stars (image should show four lighted stars out of five):
span.stars, span.stars span {
display: inline-block;
...
Your problem seems to stem from here:
span.stars, span.stars span {
display: block;
background: url(stars.png) 0 -20px repeat-x;
width: 100px;
height: 20px;
}
You've set the star container to 'display: block' which makes it take up the whole line. You want it to be an inline-block with a width smaller than the full-width available so that it can be centred.
Here is an example of how it would work:
https://jsfiddle.net/f6L8gr1b/2/
If your elements with class "col-xs-6" are supposed to be next to each other horizontally, you can use inline-block.
.col-xs-6 {
display: inline-block;
}
The div elements default to block which places them on a new line.

How to add delay in css hover on a div that was displayed none?

My first div is a simple blue square, my second div is a simple red square with display:none;. when hover the first one (the blue one) the second appears with text and image etc.. but what i want is a simple effect of delay or sliding (if possible, but if not a simple delay would be cool) i'm working on it from 2hours without any success, please any help ?
This is the jsffidle example here
this is my code :
<div class="first">
<div class="second">
<h1 class="hover-title">Hello ! </h1>
</div>
CSS :
.first{
transition-delay:2s;
width:100px;
height:100px;
background-color:blue;
}
h1{
color:gold;
}
.second{
display:none;
background-color:red;
}
.first:hover .second{
display:inline-block;
width:100%;
height:100%;
}
Thank you all.
CSS only solution
I didn't start with your example because you were missing some notions that are important to have in mind when trying to create a sliding div upon an initial one. Let me explain :
JSFiddle
HTML
<div class="content-container">
<div class="content-teaser">
Catchy teaser here
</div>
<div class="content-description">
Description that might be longer than the catchy teaser sentence <button>see more</button>
</div>
</div>
CSS
.content-container {
width : 100px;
height : 140px;
position : relative;
overflow : hidden;
}
.content-teaser {
width : 100px;
height : 140px;
background : blue;
position : absolute;
color : white;
}
.content-description {
width : 100px;
height : 140px;
background : red;
position : absolute;
margin-top : 140px;
transition : .25s;
}
.content-description:hover {
margin-top : 0px;
}
.content-teaser:hover + .content-description {
margin-top : 0px;
}
Explaination
You see 3 <div></div> :
The parent, this is the one which will help us hide the "hidden" div that is in fact marged, but you don't see it because of the property overflow : hidden
The "teaser" div that is the one which is displayed by default
The "hidden" div that is marged and so hidden because of the property right above
So the trick is to use this famous overflow : hidden. You first set all your divs, parent and children, the same width and height. Then, you want to use a special position property to put the "hidden" div on top of the "teaser" div using position : absolute for each one. So the parent will naturally have the position : relative to tell your children div to be position relatively to this div, because by default <body> is in position : relative.
Then, you applyied overflow : hidden to the parent, so when marging the future "hidden" div you will not see it.
Finally, you can use some CSS to alter an element according to the event of an other using + selector. So the following CSS :
.content-teaser:hover + .content-description {
margin-top : 0px;
}
Means :
Put a margin on the div that have the class .content-description when the div with the class .content-teaser is :hovered.
This CSS code may help you to find the solution.
.second{
display:inline-block;
height: 100%;
width: 100%;
background-color:red;
opacity: 0;
transition: opacity 0.5s ease;
}
jsfiddle
You can use css transitions like
#my_div:hover{
/*Your styles placed here*/
-moz-transition:all 1s linear;
-webkit-transition:all 1s linear;
-o-transition:all 1s linear;
transition:all 1s linear;
}
In my case 1s is the delay time, you can change that to any value you like

Recreating Medium's Image Fade Scroll Effect

Unfortunately, I'm not much of a jQuery guru, I'm trying to recreate the image fade/blur featured on medium. Here's an example -https://medium.com/matter/76d9913c6011
If you scroll down the page you'll notice some images, that progressively fade in/out as you scroll. Upon inspecting the code, I noticed there are two version of the image to create the effect, a regular version and another which has been blurred.
As far as I know, things are setup like this:
<section class=
"section-bottom-center section-image-full section-using-canvas"
data-scroll="native">
<div class="section-background" data-height="2130" data-image-id=
"1*ladXngaMeWEqp1R18uSSQA.jpeg" data-scroll="post-section" data-width=
"3200">
<div class="section-background-image" style=
"background-image: url(https://d262ilb51hltx0.cloudfront.net/max/700/gradv/29/81/40/1*ladXngaMeWEqp1R18uSSQA.jpeg);">
</div><br>
</div>
<div class="section-inner layout-single-column">
<p>SOME TEXT THAT FADES OVER THE IMAGE</p>
</div>
</section>
I found some of the jQuery, unfortunately I don't really understand it that well, so I can't recreate it.
var qm = function(a, b) {
var c = '\x3cdiv class\x3d"section-background' + (a.hasFocus ? " media-has-focus" : "") + '"' + ("section-image-full" == a.imageLayout ? ' data-scroll\x3d"post-section"' : "") + 'data-image-id\x3d"' + (0,z.N)(a.backgroundImage.id) + '"', d;
d = a.backgroundImage;
d = (d.originalWidth ? ' data-width\x3d"' + (0,z.N)(d.originalWidth) + '"' : "") + (d.originalHeight ? ' data-height\x3d"' + (0,z.N)(d.originalHeight) + '"' : "") + (d.filter ? ' data-filter\x3d"' + (0,z.N)(d.filter) + '"' : "") + (d.backgroundSize ? ' data-image-style\x3d"' + (0,z.N)(d.backgroundSize) + '"' : "");
c = c + d + "\x3e";
c = "section-image-full" == a.imageLayout ? c + ('\x3cdiv class\x3d"section-background-image" style\x3d"background-image: url(' + (0,z.N)((0,z.P)(b.miroUrl)) + "/max/" + (0,z.N)("700".replace(ji, ki)) + "/gradv/29/81/40/" + (0,z.N)(String(a.backgroundImage.id).replace(ji, ki)) + ');"\x3e\x3c/div\x3e\x3cbr\x3e') : "section-image-left" == a.imageLayout || "section-image-right" == a.imageLayout ? c + ('\x3cimg src\x3d"' + (0,z.N)((0,z.P)(b.miroUrl)) + "/max/" + (0,z.N)(b.postColumnWidth) + "/multiply/grey/" +
(0,z.N)(a.backgroundImage.id) + '"\x3e') : c + "\x3cbr\x3e";
return c + "\x3c/div\x3e";
};
One thing I did notice, is rather than the the two images simply fading in/out - the fading level is somehow bound to the scroll, so when you stop scrolling the images stop fading. This is a really nice effect.
P.S I'm looking to do this as simply as possible, preferably without jQuery plugins.
Thanks
Check out Andreas Storm's Medium Blur scrolling effect on Codepen.
HTML:
<div class='blurImg'>
<div style="background-image: url('https://d262ilb51hltx0.cloudfront.net/fit/c/1600/1280/gradv/29/81/60/darken/25/1*4ncz3hLxmL8E_bUh-0z62w.jpeg')"></div>
<div class='blur' style="background-image: url('https://d262ilb51hltx0.cloudfront.net/fit/c/1600/1280/gradv/29/81/40/darken/50/blur/50/1*4ncz3hLxmL8E_bUh-0z62w.jpeg')"></div>
</div>
<header>
<div>
<h1>
Medium
</h1>
<p>
Everyone’s stories and ideas
</p>
<a href='https://medium.com/' title='Medium'>Learn more</a>
</div>
<nav role='navigation'>
<ul>
<li>
<a class='active' href='#'>Reading List</a>
</li>
<li>
<a href='#'>Bookmarks</a>
</li>
<li>
<a href='#'>Top 100</a>
</li>
</ul>
</nav>
</header>
<div class='container'>
<div></div>
</div>
CSS:
body
font-size 14px
font-family Sans-Serif
*
box-sizing border-box
a
text-decoration none
.blurImg
position relative
width 100%
height 440px
z-index -1
top 0
left 0
& > div
position fixed
width 100%
height 440px
background-repeat no-repeat
background-size cover
background-position center center
.blur
opacity 0
header
z-index 1
position absolute
top 0
width 100%
padding 0px 20px
& > div
max-width 600px
margin 0 auto
padding-top 150px
height 380px
text-align center
color White
a
font-size 0.8em
letter-spacing 0.08em
color rgba(255,255,255,0.85)
line-height 30px
padding 7px 14px
border 1px solid rgba(255,255,255,0.3)
border-radius 2em
transition all 0.3s ease
&:hover
background white
color Gray
p
font-size 1.5em
margin-bottom 0.7em
font-family Times New Roman
h1
font-weight 800
font-size 3.4em
margin-bottom 0.2em
nav
max-width 600px
margin 0 auto
height 60px
border-top 1px rgba(255,255,255,0.35) solid
ul
li
display inline-block
margin-right 20px
a
font-weight 800
font-size 11px
text-transform uppercase
letter-spacing .2em
color rgba(255,255,255,0.5)
transition color 0.3s linear
line-height 60px
display block
&.active
box-shadow 0px -1px 0px white
nav ul li a.active, nav ul li a:hover
color white
.container
height 1300px
background white
z-index 1
padding 0px 20px
div
max-width 600px
margin 0 auto
padding-top 40px
CoffeeScript:
$(window).scroll ->
oVal = ($(window).scrollTop() / 240)
$(".blur").css "opacity", oVal
I also was trying to figure out how they managed this at Medium.
The minified javascript snippet you are pointing out doesn't seems to me being responsible for the blur effect. In my opinion they also process through a canvas.
Check out this SO answer:
Coderwall blurred background effect with canvas
hope that helps. (unfortunately this uses a bit of jquery but you can skip that part ;) )

Categories