Change CSS of inner div when scroll reaches that div - javascript

I am attempting to implement a scroll function where the CSS of the inner div's change when it reaches a certain height from the top.
var $container = $(".inner-div");
var containerTop = $container.offset().top;
var documentTop = $(document).scrollTop();
var wHeight = $(window).height();
var minMaskHeight = 0;
var descriptionMax = 200;
var logoMin = -200;
var maskDelta = descriptionMax - minMaskHeight;
var $jobOverview = $container.find(".right");
var $jobLogo = $container.find(".left");
var curPlacementPer = ((containerTop - documentTop) / wHeight) * 100;
var topMax = 85;
var center = 20;
var bottomMax = -15;
//console.log("Placement: " + curPlacementPer);
function applyChanges(perOpen) {
var maskHeightChange = maskDelta * (perOpen / 100);
var opacityPer = perOpen / 100;
var newDescriptionLeft = descriptionMax - maskHeightChange;
var newLogoLeft = logoMin + maskHeightChange;
if (newDescriptionLeft <= 0) newDescriptionLeft = 0;
if (newLogoLeft >= 0) newLogoLeft = 0;
if (opacityPer >= 1) opacityPer = 1;
$jobOverview.css({
transform: "translate(" + newDescriptionLeft + "%,-50%)",
opacity: opacityPer
});
$jobLogo.css({
transform: "translate(" + newLogoLeft + "%,-50%)",
opacity: opacityPer
});
}
if (window.innerWidth > 640) {
$container.removeClass("mobile");
// console.log("Placement: " + curPlacementPer);
if (curPlacementPer <= topMax /*&& curPlacementPer >= center*/ ) {
var perOpen = ((topMax - curPlacementPer) / 25) * 100;
applyChanges(perOpen);
} else if (curPlacementPer < center /*&& curPlacementPer >= bottomMax*/ ) {
var perOpen = (((bottomMax - curPlacementPer) * -1) / 25) * 100;
applyChanges(perOpen);
} else {
$jobOverview.css({
transform: "translate(200%,-50%)",
opacity: "0"
});
$jobLogo.css({
transform: "translate(-300%,-50%)",
opacity: "0"
});
}
<div class="outer-div">
<div class="inner-div first">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div second">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div third">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div fourth">
<div class="left"></div>
<div class="right"></div>
</div>
</div>
Currently, all of the inner div's gets changed at the same time.
I noticed that when I change the $container class to equal '.first' and specify it more, it works.
Is there any way to make the inner div's change separately, relative to its height from the top? Any way I can iterate the scroll function so I can add more inner div's in the future and not have to worry about changing my scroll function?

In raw JavaScript, this is my answer:
// Define the element -- The '#fooBar' can be changed to anything else.
var element = document.querySelector("#fooBar");
// Define how much of the element is shown before something happens.
var scrollClipHeight = 0 /* Whatever number value you want... */;
// Function to change an element's CSS when it is scrolled in.
const doSomething = function doSomething() {
/** When the window vertical scroll position plus the
* window's inner height has reached the
* top position of your element.
*/
if (
(window.innerHeight + window.scrollY) - (scrollClipHeight || 0) >=
element.getBoundingClientRect().top
)
// Generally, something is meant to happen here.
element.style = "/* Yay, some CSS! */"
};
// Call the function without an event occurring.
doSomething();
// Call the function when the 'window' scrolls.
addEventListener("scroll", doSomething, false)
This is the method I use. If there are other methods, I'd love to see them as well but this is my answer for now.

consider using 3rd party jQuery plugin for easier job, like one of these:
https://github.com/xobotyi/jquery.viewport
or
https://github.com/zeusdeux/isInViewport
then you can have additional element selector e.g.: ":in-viewport"
so you can:
$(window).on('scroll',function() {
$('div').not(':in-viewport').html('');
$('div:in-viewport').html('hello');
});

Check if current scroll offset from top is bigger than the element offset from the top:
$(window).scroll(function() {
var height = $(window).scrollTop();
var element = $('#changethis'); //change this to your element you want to add the css to
if(height > element.offset().top) {
element.addClass('black'); //add css class black (change according to own css)
}
});
Html:
<div id="changethis">Test</div>
Css:
body
{
height:2000px;
}
.black
{
background-color:black;
color:white;
padding:20px;
}
Demo:
https://codepen.io/anon/pen/WZdEap
You could easily implement this in your existing code.

Below is the sample snippet code, Hope it'll work for you:
$(document).ready(function(){
topMax = 100;
topMin = 25;
$(document).scroll(function(){
$('.inner-div').each(function(){
if($(this).offset().top-$(window).scrollTop()<=topMax && $(this).offset().top-$(window).scrollTop()>=topMin){
$(this).css({'background':'#c7c7c7'});
}else{
$(this).css({'background':'inherit'});
}
});
});
});
div{
width:100%;
border:1px solid red;
padding:5px;
}
div.inner-div{
border: 1px dashed green;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer-div">
<div class="inner-div first">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div second">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div third">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div fourth">
<div class="left"></div>
<div class="right"></div>
</div>
</div>
Happy to help you! :)

Related

Animation and transition property with setTimeout() function

I'm fairly new to JS, and I'm trying to make a simple text slide animation. However, when the animation event is activated the first time, the transition property doesn't apply. Every time after that it works fine.
So I'm struggling with the first execution of the slide animation.
Here is the code:
function slide_animation(direction){
// Init
let text = document.querySelector(".story .box .right-part .text");
let text_html = text.innerHTML;
let text_slide = [
"Text n°1",
'Text n°2',
'Text n°3'
];
let current_slide = 0;
// Looking for the current_slide
for(let i=0;i<text_slide.length;i++){
if (text_slide[i]==text_html){current_slide = i};
}
// Calculating the next slide position
if (direction=='right'){
if (current_slide >= text_slide.length-1){
current_slide = 0;
}
else {current_slide+=1;}
}
else {
if (current_slide <= 0){
current_slide = text_slide.length-1;
}
else {
current_slide = current_slide-=1;
}
}
// Animation
setTimeout(()=>{
text.style.transition = '0.5s';
text.style.opacity = 0;
text.style.left = '100px';
},250);
setTimeout(()=>{
text.innerHTML = text_slide[current_slide];
text.style.opacity = 1;
text.style.left = '0px';
},750)
}
.story .box .right-part .text-container .text {
line-height: 1.25;
position: relative;
}
<div class="box">
<div class="left-part">
<div class="text-container">
<p class="text">
<span class="text-padding fontsize-md">----</span>
</p>
</div>
<div class="buttons-container">
<button class="backward" onclick="slide_animation('left')"></button>
<button class="forward" onclick="slide_animation('right')"></button>
</div>
</div>
<div class="right-part">
<div class="text-container">
<p class="text fontsize-sm">Text n°1</p>
</div>
</div>
</div>
Anyway , it is fixed. I added another setTimeOut that did nothing really useful on the text , i placed it just before the others setTimeOut and it seems to work out properly.
Here is what is inserted :
setTimeout(()=>{
text.style.left=0;
},0)

How to set an attribute as percentage in jQuery?

I am unable to set css attribute {left: $numberEquivalentToPercent} in jQuery
var targets = $('.parallax__layer__cell');
var i = 1;
for(i = 1; i <= targets.length; i++)
{
if (targets.parents('.parallax__layer--bg').length) {
//apply to only those element that have a parent having class "parallax__layer--bg"
targets.eq(i).css('left', toString(60*i)+ "%");
}
}
The above code(in JS) is expected to produce the same effect as below(in CSS)
.parallax__layer__cell:nth-child(1) { left: 0%; }
.parallax__layer__cell:nth-child(2) { left: 60%; }
.parallax__layer__cell:nth-child(3) { left: 120%; }
.parallax__layer__cell:nth-child(4) { left: 180%; }
.parallax__layer__cell:nth-child(5) { left: 240%; }
Basically I am trying to convert a static code to dynamic code
What if you put 60 * i into a variable called numPercent and then do numPercent.toString() to convert it to a string?
var targets = $('.parallax__layer__cell');
var i = 1;
for(i = 1; i <= targets.length; i++)
{
if (targets.parents('.parallax__layer--bg').length) {
//apply to only those element that have a parent having class "parallax__layer--bg"
var numPercent = 60*i;
targets.eq(i).css('left', numPercent.toString() + "%");
}
}
Here is working code:
var targets = $('.parallax__layer__cell');
var i ;
for(i = 0; i < targets.length; i++)
{
if (targets.eq(i).parents('div.parallax__layer--bg').length) {
targets.eq(i).css('left', 60*i + "%");
}
}
.parallax__layer__cell{
position:absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<div class="parallax__layer--bg">
<div class="parallax__layer__cell">a</div>
</div>
<div class="parallax__layer--bg">
<div class="parallax__layer__cell">b</div>
</div>
<div class="parallax__layer--bg">
<div class="parallax__layer__cell">c</div>
</div>
<div class="parallax__layer--bg">
<div class="parallax__layer__cell">d</div>
</div>
<div class="parallax__layer--bg">
<div class="parallax__layer__cell">e</div>
</div>
<div>
<div class="parallax__layer__cell">--no parent--</div>
</div>
</div>
I believe this is a timing issue between parallax script and your script. Since css loads first parallax script works fine. But if you remove css and run your script after parallax script it may not do the same effect. Make sure your script runs before actual parallax script.

Display text in time intervals with CSS and JS

I would like to create, using the setInterval() JS function, a visual effect that displays text one character at the time with an interval of 100ms per character, on an Angular application.
Note that this happens in the index.html within the <app-root> tags, so it will appear only while the app is bootstrapped.
After reading the setInterval() page i thought that this would make the job, so this is my code:
var divs=['rBox','aBox','tBox','sBox'];
var index=-1;
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
var fun = setInterval(display,100);
}
function display(){
if(index < 4){
document.getElementById(divs[++index]).style.visibility='visible'
}else{
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
But it does not display anything, the divs containing the numbers are there with visibility set to hidden but it seems like they are never set to visible
I can't see where the problem lies. If I look at the code from an algorithmic point of view, I guess I probably don't understand very well the inner working of setInterval().
fun was not declared globally
And index was incremented too much
A rough update to the code:
var divs=['rBox','aBox','tBox','sBox'];
var index=-1;
var fun
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
fun = setInterval(display,100);
}
function display(){
if(index < 3){
document.getElementById(divs[++index]).style.visibility='visible'
}else{
clearInterval(fun);
}
}
displayChars()
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
After minor modifications it's working, see below. I changed index to start at 0 and to be used with divs[index++] (so use-then-increment), and to compare it with divs.length instead of hardcoded 4. Also I put variable fun at the global level.
var divs = ['rBox', 'aBox', 'tBox', 'sBox'];
var index = 0;
var fun = -1; // timer handle
function displayChars() {
for (container of divs) {
document.getElementById(container).style.visibility = 'hidden';
}
fun = setInterval(display, 100);
}
function display() {
if (index < divs.length) {
document.getElementById(divs[index++]).style.visibility = 'visible'
} else {
clearInterval(fun);
}
}
displayChars();
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
Your variable fun needs to be global, otherwise display() can't
access it
Don't forget to declare container in your for...of loop as an actual variable
You where incrementing index too often
var divs = ['rBox', 'aBox', 'tBox', 'sBox'];
var index = -1;
var fun;
function displayChars() {
for (var container of divs) {
document.getElementById(container).style.visibility = 'hidden';
}
fun = setInterval(display, 100);
}
function display() {
if (index < 3) {
document.getElementById(divs[++index]).style.visibility = 'visible';
} else {
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
Pure CSS solution, if an option, looks something like this:
.rats-div > div {
opacity: 0; /* or "visibility: hidden" */
animation: opacity .1s forwards;
}
.rats-div > div:nth-child(2) {animation-delay: .1s}
.rats-div > div:nth-child(3) {animation-delay: .2s}
.rats-div > div:nth-child(4) {animation-delay: .3s}
#keyframes opacity {
to {opacity: 1} /* or "visibility: visible / initial" */
}
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
var divs=['rBox','aBox','tBox','sBox'];
var index=0;
var fun=null;
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
fun = setInterval(display,100);
}
function display(){
if(index < 4){
document.getElementById(divs[index]).style.visibility='visible'
index++;
}else{
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
try this code. it should do your job.

Insert div on next line when element is clicked

I am trying to make something similar to what you find in google images. When a picture is clicked, a div with the image appears on the next line over the other images that is under the clicked one.
I have a set of divs with float:left and position:relative. They have different widths. When i click on a div i want a new full width div to appear on the next line. The divs under the clicked one should be bushed down under the full width one.
I tried to do this by looping through the divs and compare the position of the divs to the clicked one like this:
$(".Wrapper").on("click", ".testDivs", function () {
var thisTop = $(this).position().top;
$(".testDivs").each(function(i, obj) {
var otherTop = $(obj).position().top;
if(thisTop < otherTop){
$(".fullWidthDiv").insertBefore(obj);
return;
}
});
});
This doesn't work and I don't really know how I should do this. Any tips/solutions?
This requires a lot of information to explain. So I'd rather suggest reading a blog post on this topic.Hope this will help you.
https://www.sitepoint.com/recreating-google-images-search-layout-css/
Here is a way to achieve that. It will not keep scroll position but that would be another easy fix.
<div class="wrapper">
<div class="row1 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
<div class="row2 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
<div class="row3 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
</div>
I only applied some styling ti increase visibility of the changes.
.img {
width: 32%;
height: 100px;
background-color: #ccc;
display: inline-block;
}
.row {
border: 1px solid green
}
.big-img {
height: 300px;
}
And finally the JS:
$('.img').click(function() {
var expandedImg = '<div class="big-img"></div>';
$('.big-img').remove();
$(this).parent().append(expandedImg);
})
Fiddle: https://jsfiddle.net/a5fm2dup/
why don't you just do
$(".Wrapper").on("click", ".testDivs", function () {
$(this).after($(".fullWidthDiv"));
});
Ended up making a solution based on my initial code. This doesn't require all the CSS the other solution that was postet suggested. Adding rows dynamically was also suggested, but this became very complicated when making it responsive to window resizing. Feel free to reply if you have any comments on this
function positionDiv() {
var checker = false;
var n;
var thisTop = clickedElement.position().top;
$(".testDivs").each(function(i, obj) {
var otherTop = $(obj).position().top;
if(thisTop < otherTop){
$(".testDivs:eq(" + (i-1) + ")").after($(".fullWidthDiv"));
checker = true;
return false;
}
n = i;
});
if (!checker) {
$(".testDivs:eq(" + n + ")").after($(".fullWidthDiv"));
}
}
var clickChecker = null;
$(".wrapper").on("click", ".testDivs", function () {
if (clickChecker === this){
$(".fullWidthDiv").hide();
clickChecker = null;
} else {
$(".fullWidthDiv").show();
clickChecker = this;
}
clickedElement = $(this);
positionDiv();
});
window.onresize = function(event) {
if( clickedElement != null) {
$(".tagTextWrapper").hide();
positionDiv();
$(".tagTextWrapper").show();
}
}

HTML / JS horizontal scrolling DIV "list"

What I want to make is an horizontally scrolling DIV "list" just like pretty much every big web site in the internet(netflix for example).
I tried to make it using a main DIV which would be some kind of container, a 2nd div which holds all the content and is inside the first DIV and a lot of DIVs, one for each content module, that go inside the 2nd div.
the parts of the 2nd DIV that overflow the main one should hide, and the content could be shown by moving it(the 2nd DIV).
this is the best I could come up with, but it still doesn't work jsfiddle
This is my HTML
<button onmouseover="left=1" onmouseout="left=0">
<</button>
<div class="container">
<div id="filler" style="left:0px">
<div class="module" style="background:coral;">testing</div>
<div class="module" style="background:lightblue;">testing</div>
<div class="module" style="background:lightgreen;">testing</div>
<div class="module" style="background:salmon;">testing</div>
<div class="module" style="background:lightyellow;">testing</div>
</div>
</div>
<button onmouseover="right=1" onmouseout="right=0">></button>
CSS
.container {
height:50px;
width:200px;
overflow:hidden;
}
#filler {
height:50px;
width:250px;
position:relative;
border-radius:10px;
background:crimson;
}
.module {
width:50px;
height:50px;
border-radius:5px;
float:left;
line-height:50px;
text-align:center;
}
JavaScript:
var feft = 0
//feft stands for filler left
var right = 0
var left = 0
var loaded = 0
window.onload=function(){
loaded=1
}
function move() {
if(loaded == 1){
if (left == 1 && feft <= 250) {
//left == 1 && feft <= filler width
document.getElementById("filler").style.left = feft + 1
}
if (right == 1 && feft >= 0) {
//right == 1 && feft >= 0
document.getElementById("filler").style.left = feft - 1
} //these IFs tests if the filler should move
feft = document.getElementById("filler").style.left
//this sets the feft variable to what it needs to be for the next run of the function
}}
window.setInterval(move(), 100)
I have made a fiddle for you.
demo
HTML code
<button onmouseover="left=1" onClick="move(-1)"><</button>
<div class="container">
<div id="filler" style="left:0px">
<div class="module" style="background:coral;">testing</div>
<div class="module" style="background:lightblue;">testing</div>
<div class="module" style="background:lightgreen;">testing</div>
<div class="module" style="background:salmon;">testing</div>
<div class="module" style="background:lightyellow;">testing</div>
</div>
</div>
<button onmouseover="right=1" onClick="move(1)">></button>
JS Code
var position = 0;
var moduleCount = document.querySelector(".module").length;
window.move = function(number) {
if (number) {
position += number;
if (number == 0 || number > moduleCount) {
position = 0;
}
} else {
if (position <= 4) {
position++;
} else {
position = 0;
}
}
moduleOffset = document.querySelector(".module").offsetWidth;
filler = document.querySelector("#filler");
filler.style.left = -( position* moduleOffset) + "px";
}
setInterval(window.move, 3000);
What you want to do is called "Carousel". I suggest to use bootstrap for example and implement it then in your site.
http://getbootstrap.com/javascript/#carousel
Try adding overflow: scroll as a CSS property to your container div.

Categories