How to make jquery scrolling effect work on multiple element/classes? - javascript

I'm trying to make a very light script for multiple classes class"demo" that can work on my onScrollDown responsive animation.
I don't really understand about writing arrays. but, I believe that if I use document.getElementsByClassName("demo")[i] , i < 0 and some function(i) I can implement it for individual classes. Because I use getBoundingClientRect() instead of fixed value.
So, how can I write it correctly using i as arrays?
Thank you..
Here is my working script :
<script>
var e = document.getElementById("demo");
var rect = e.getBoundingClientRect();
var x = rect.top;
$(window).bind('scroll', function () {
if ($(window).scrollTop() > x-300) {
$('#demo').addClass('animate');
} else {
$('#demo').removeClass('animate');
}
});
</script>
*work only for a single element.
Here is what I'm trying to do, that not working yet
<script>
var e = document.getElementsClassName("test")[i];
var rect = e.getBoundingClientRect();
var x = rect.top;
var i;
for (i = 0; i < e.length; i++) {
$(window).bind('scroll', function (i) {
if ($(window).scrollTop() > x-300) {
$e.addClass('animate');
} else {
$e.removeClass('animate');
}
});
}
</script>
CSS :
<style>
.test {
background:#345;
color:#FFF;
height:2em;
padding:.5em;
top:50px;
margin-top: 100px;
width:100%;
}
.animate {
width: 60px;
}
</style>
HTML
<div style="color: red; margin-bottom: 400px;">(Top!)</div>
<div class="test" id="demo">Menu</div>
<div class="test" id="demo">Menu</div>
<div class="test" id="demo">Menu</div>
<div style="color: red; margin-top: 400px;">(Bottom!)</div>

Okay so I've achieved what you're trying to do. Here are the changes I made:
Used the JQuery each function. This will loop all of the demo elements every time a scroll is detected. There are other ways of looping the elements but because you've already imported JQuery we may as well use it's functions.
Changed #demo to .demo. In other words, I've changed id to class. id should only be used when working with elements that are completely unique. In this case, there are multiple demos so we use class instead.
Final code (as you scroll each element will turn red showing that the animate class has been added:
$(window).on('scroll', function() {
$('.demo').each(function(i, obj) {
var rect = obj.getBoundingClientRect();
var x = rect.top;
if ($(window).scrollTop() > x - 300) {
$(obj).addClass('animate');
} else {
$(obj).removeClass('animate');
}
});
});
.body {
height: 200vh;
background-color: #f1f1f1;
}
.demo {
height: 200px;
width: 100%;
background-color: #f9f9f9;
}
.demo.animate {
background-color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="body">
<div class="demo"></div>
<div class="demo"></div>
<div class="demo"></div>
<div class="demo"></div>
</div>

There are few notes in regards to your code:
In jQuery you can get elements offset by using .offset() function.
you should not use the same id more than once per page.
.bind() has been deprecated since jQuery 3.0. Use .on() instead.
To toggle class you can use .toggleClass(className, state). State is used to determine if you want to remove or add the class.
See this example:
$(window).on('scroll', function() {
jQuery(".test").each(function() {
let isTop = $(window).scrollTop() > jQuery(this).offset().top - 300;
jQuery(this).toggleClass('animate', isTop);
});
});
.test {
background: #345;
color: #FFF;
height: 2em;
padding: .5em;
top: 50px;
margin-top: 100px;
width: 100%;
}
.animate {
width: 60px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="color: red; margin-bottom: 400px;">(Top!)</div>
<div class="test">Menu</div>
<div class="test">Menu</div>
<div class="test">Menu</div>
<div style="color: red; margin-top: 400px;">(Bottom!)</div>

Related

How to change the text color of a transparent header on scroll, depending on the div it overlaps

I have a header with a transparent backgrounsd, and I am trying to get the text of the header to change colour between white and black depending on the background of the div it's overlapping.
So far I have managed to add a class of .color-menu to all the divs where I want the header to be black.
I then have it add a class of .dark-menu to the header when the .color-menu div reaches the top of the page.
The problem is that it only works for the first .colour-menu div. It will change to black when it is in the viewport and back to white for the next div but then when the next .color-menu div gets to the top it doesn't change.
So, it seems like the .each function isn't working but I am not sure how to fix it.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
UPDATE: I have also tried using $(this) but it really throws off when it changes color.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(this).offset().top;
var bottom_of_element = $(this).offset().top + $(this).outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
Here is a simplified version of my code as an example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
});
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0,0,0,0);
margin: 0;
padding:10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.dark-menu a{
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white, .white-bold, .light, .light-bold, .bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class ="black"></div>
<div class ="white"></div>
<div class ="black"></div>
<div class ="white-bold"></div>
<div class ="black"></div>
<div class ="light"></div>
<div class ="black"></div>
<div class ="light-bold"></div>
<div class ="black"></div>
<div class ="bright"></div>
What is happening in your code is that on scroll, you loop through every color-menu div and add the class if it is the current one... but then the code continues to loop though the remaining elements in the array and removes it again because the page is not in the other div.
I've explained step-by-step the changes you need to get this to work after the example, but first you can see it working here:
Working Example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
$(window).scroll(function() {
var inColorMenu = false; /* initialise var to store if we are in color-menu */
var top_of_screen = $(window).scrollTop(); /* just get this once outside loop */
/* Loop through each color-menu element and check if we are in one */
$('.color-menu').each(function(i) {
var top_of_element = $(this).offset().top;
var bottom_of_element = top_of_element + $(this).outerHeight();
/* if we are in a color-menu element, set our var to true and stop processing */
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
});
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0, 0, 0, 0);
margin: 0;
padding: 10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.header.dark-menu a {
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white,
.white-bold,
.light,
.light-bold,
.bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class="black"></div>
<div class="white"></div>
<div class="black"></div>
<div class="white-bold"></div>
<div class="black"></div>
<div class="light"></div>
<div class="black"></div>
<div class="light-bold"></div>
<div class="black"></div>
<div class="bright"></div>
How this works:
Declare a variable to record whether we are in a "color-menu" class or not, and initialise this to false, e.g.:
var inColorMenu = false;
When looping through $('.color-menu').each, if we are between the top and bottom of one of divs (which your code is already detecting), then set our variable to true to record this.
We can also return false to break the each loop and stop processing the rest of the elements (it will still work without this, we are just reducing the amount of processing required):
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
Finally, after we finish our $('.color-menu').each loop, if inColorMenu is true, we know we are in a color-menu div so we add the dark-menu class to the header, otherwise we remove it:
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
Note: You need to use $(this) when getting the offset().top and outerHeight() so that you are getting the values for the current element in the loop. $(".color-menu") gets the values for an unspecified element with this class so will not work.

How to add width to an element using jquery each function?

I want to build a loading bar effect using two seperate divs inside each other. I got it all positioned and all that but how can I make one of them increase its width from %1 to %100 with transition? I want it to be filled in 10 sec.
Thanks.
<div class="loading-container">
<div class="outside-loading"></div>
<div class="inside-loading"></div>
</div>
Fairly simple with jQuery animate() which you can customize for step or easing and also use callbacks for start or complete as needed
$('.outside-loading').animate({width: '100%'}, 3000);// using 3 sec for demo
.outside-loading {
background: blue;
width: 0;
height: .5em
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="loading-container">
<div class="outside-loading"></div>
<div class="inside-loading"></div>
</div>
Vanilla Javascript
Create a function and increase the width with your set interval function. Add a conditional that checks if the width is 100% and if it is, then clear the interval. I also moved the divs within each other and set the display of the inner span tag to inline-block...
You can also target the elements textContent and display the widths progress in percent as well...
var i = 0;
function move() {
if (i == 0) {
i = 1;
var elem = document.getElementsByClassName("inside-loading");
var width = 1;
var id = setInterval(frame, 100);
function frame() {
if (width >= 100) {
clearInterval(id);
i = 0;
} else {
width++;
elem[0].style.width = width + "%";
elem[0].textContent = width + "%";
}
}
}
}
move();
.outside-loading {
width: 100%;
height: 30px;
background-color: grey;
}
.inside-loading {
display: inline-block;
width: 1%;
height: 100%;
background-color: red;
text-align: right;
vertical-align: middle;
font-size: 30px;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
}
<br><br><br>
<div class="loading-container">
<div class="outside-loading">
<span class="inside-loading">
</span>
</div>
</div>

How to change content of div on hover using JQuery/Javascript

I'm trying to change the contents of a div when it's hovered over using JQuery. I've seen answers on stack overflow, but I can't seem to get it working.
I've tried
$( "imgDiv" ).mouseover(
function() {
$("tdiv").textContent = "hovering";
},
function() {
$("tdiv").textContent = 'title';
}
);
I've also replaced "mouseover" with "hover". I've used a variable and the actual div in place of "imgDiv".
This is what my code looks like:
imgDiv = document.getElementById('imgDiv');
tDiv = document.getElementById('titleDiv');
$( "imgDiv" ).mouseover(
function() {
$("tdiv").textContent = "hovering";
}, function() {
$("tdiv").textContent = 'title';
}
);
body {
background: white;
padding: 20px;
font-family: Helvetica;
}
#imgDiv {
width: 100px;
height: 100px;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="imgDiv">
<div id=titleDiv>title</div>
</div>
You can use jQuery's .hover() function along with the .text() function to do what you want. Also, no need for document.getElementById:
$("#imgDiv").hover(
function() {
$("#titleDiv").text("hovering");
},
function() {
$("#titleDiv").text('title');
}
);
body {
background: white;
padding: 20px;
font-family: Helvetica;
}
#imgDiv {
width: 100px;
height: 100px;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="imgDiv">
<div id="titleDiv">title</div>
</div>
You can target the div with jQuery, and store it's original value. On mouseout, you can restore it. Also using mouseenter reduces the number of times the logic processes as mouseover will fire for every mouse move over the element.
var $titleDiv = $('#titleDiv');
$("#imgDiv")
.on('mouseenter', function() {
$titleDiv.data('originalText', $titleDiv.text());
$titleDiv.text('hovering');
})
.on('mouseout', function() {
$titleDiv.text($titleDiv.data('originalText'));
});
body {
background: white;
padding: 20px;
font-family: Helvetica;
}
#imgDiv {
width: 100px;
height: 100px;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="imgDiv">
<div id="titleDiv">title</div>
</div>
First of all, replace $("imgDiv") with $("#imgDiv") to get the element with id (#) imgDiv.
Then $("tdiv") doesn't exist, you probably mean $("div") to select a <div>tag in your DOM.
And finally, $("tdiv").textContent doesn't exist. You can try $("div").html() or $("div").text() to get the <div> tag content
--
Quick reminder : jQuery documentation on selectors
$("div") will select the <div> tags
$(".element") will select tags with class="element"
$("#element") will select tags with id="element"
You need to try like this
$( "#imgDiv" ).mouseover(function() {
$("#titleDiv").text("hovering");
}).mouseleave( function() {
$("#titleDiv").text('title');
});
body {
background: white;
padding: 20px;
font-family: Helvetica;
}
#imgDiv {
width: 100px;
height: 100px;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="imgDiv">
<div id=titleDiv>title</div>
</div>
Easy solution,
var s = document.getElementsByTagName('div');
function out() {
s[0].innerHTML = 'hello';
}
function ibn() {
s[0].innerHTML = 'Myname';
}
<div onmouseout = 'out()' onmouseenter = 'ibn()'> Myname </div>
You cannot call reference a dom with pure Javascript and them manipulate it with jQuery - it will not work.
Try this:
$( "#imgDiv" ).mouseover(function() {
$("#titleDiv").text("hovering");
});
The titleDiv id has to be referenced in your code using "#", then the id name.
Also, use $("#name_of_id").text("your content") instead of .textContent()

Javascript: can't change background on div click or change marginLeft on element

I tried to test changing backgroundColor and marginLeft on this simple example: https://jsfiddle.net/ntqLo6v0/2/
and couldn't make it work.
var collapsed = 0;
$('[data-toggle=collapse-button]').click(function() {
if (collapsed == 0) {
close();
} else {
open();
}
});
function close() {
document.getElementById("button").style.backgroundColor = "blue";
(document.getElementsByClassName("content")[0]).style.marginLeft = "20px";
collapsed = 1;
}
function open() {
document.getElementById("button").style.backgroundColor = "red";
(document.getElementsByClassName("content")[0]).style.marginLeft = "120px";
collapsed = 0;
}
.content {
background-color: green;
width: 200px;
height: 100px;
}
#button {
background-color: red;
width: 100px;
height: 50px;
margin: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="button" data-toggle="collapse-button">
button
</div>
<div class="content">
some content here
</div>
There is just a little issue: $('[data-toggle=collapse-button]').
You are using jQuery but do not define it. That's why you get a Uncaught ReferenceError: $ is not defined in the console.
Here is your updated fiddle where I added jQuery (in the resources left) in order to make your example running.

Fill parent div with the image being hovered over

I am still fairly new to JS, and I am trying to replace the HTML of a div with a picture that is being moused over, and when the mouse leaves I want it to return to it's normal state. I thought that I did everything right but my code doesn't seem to be working. I've looked through stack overflow and I see a lot of jQuery solutions to my 'problem,' but I would like an answer in pure JavaScript (I'm trying to "maser" this first), along with an explanation so I can understand why the answer IS the answer. Thanks.
I'll try to explain myself (my code). I grabbed reference to the image holder, and I grabbed reference to the the images. I thought I made a function that looped through the array of images and added an event listener to whichever image ( image[i] ) was being moused over. Then, I added an event listener that is supposed to return the image holder to it's default state by inserting the original HTML. I just don't understand how to fix this.
var holder = document.getElementById('holder');
var images = document.getElementsByTagName('img');
var popImage = function () {
for (i = 0; i < images.length; i++) {
images[i].addEventListener('mouseover', = function () {
holder.innerHTML = images[i];
});
images[i].addEventListener('mouseout', function () {
holder.innerHTML =
'<div class='col-md-3 img-fluid' id='img1'><img src='photo1.jpg'></div>
<div class='col-md-3 img-fluid' id='img2'><img src='photo2.jpg'></div>
<div class='col-md-3 img-fluid' id='img3'><img src='photo3.2.jpg'></div>
<div class='col-md-3 img-fluid' id='img4'><img src='photo4.jpg'></div>'
});
};
};
popImage();
You said you are new to JS and just learning which is great but an important part of learning JS is learning when not to use it. As #Yoda said if this was for production you really should use CSS instead of JS.
Here is one way you could accomplish this with pure CSS
<style>
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
float: left;
}
.holder:hover > .img {
opacity: 0;
}
.holder:hover > .img:hover {
opacity: 1;
}
</style>
<div class="holder">
<!-- Using div.img for simplicity, these whould be your <img/> tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
For the purpose of learning, here's how you'd do it in JS:
var holder = document.getElementById('holder');
var images = document.querySelectorAll('.img');
var filter = false;
function popImage () {
// Use for (var i = 0 . . .
// Instead of for (i = 0 . . .
// Because without var, i will be stored in the global scope
for (var i = 0; i < images.length; i++) {
(function (_i) {
images[_i].addEventListener('mouseover', function () {
holder.innerHTML = '';
// We can't set innerHTML to images[_i]
// because it's a DomNode not a string
holder.appendChild(images[_i]);
});
})(i);
}
holder.addEventListener('mouseout', function (e) {
if (e.target !== holder)
return;
holder.innerHTML = '';
// Again, use var j = 0 . . .
for (var j = 0; j < images.length; j++) {
holder.appendChild(images[j]);
}
});
}
popImage();
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
display: inline-block;
}
#holder {
position: relative;
width: 100%;// So doesn't collape and trigger mouseout
height: 100px;
background: red;
padding: 20px 0;
}
<div id="holder">
<!-- Again, these would be your image tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
I had 10 mins before leaving work so I had a crack at this to see how I would do it and give you some ideas.
Here is my implementation (https://jsfiddle.net/hg7s1pyh/)
I guess the main thing here is that I've broken it down into lots of smaller parts, this makes solving problems far easier, each method is concerned with doing one thing only.
You will also note the use of classes to show and hide content rather than removing it entirely, this takes lots of the arduous work out of this feature.
function attachEvents() {
var images = getImages();
images.forEach(function(image) {
attachMouseOverEvent(image);
attachMouseLeaveEvent(image);
});
}
function attachMouseOverEvent(element) {
element.addEventListener('mouseover', function(e) {
var clonedImage = e.target.cloneNode();
addImageToPreview(clonedImage);
});
}
function attachMouseLeaveEvent(element) {
element.addEventListener('mouseleave', function(e) {
removeImageFromPreview();
});
}
function getImages() {
return document.querySelectorAll('.js-image');
}
function getImagePreviewElement() {
return document.querySelector('.js-image-box');
}
function addImageToPreview(imageElement) {
var previewElement = getImagePreviewElement();
previewElement.classList.add('previewing');
previewElement.appendChild(imageElement);
}
function removeImageFromPreview() {
var previewElement = getImagePreviewElement();
previewElement.classList.remove('previewing');
var image = previewElement.querySelector('.js-image');
image.remove();
}
attachEvents();
.image-box {
position: relative;
min-height: 400px;
width: 400px;
border: 1px solid #000;
text-align: center;
}
.image-box .placeholder {
position: absolute;
top: 50%;
text-align: center;
transform: translateY(-50%);
width: 100%;
}
.image-box.previewing .placeholder {
display: none;
}
.image-box .image {
position: absolute;
top: 50%;
text-align: center;
transform: translate(-50%, -50%);
height: 100%;
width: 100%;
}
.images {
margin-top: 10px;
}
<div class="js-image-box image-box">
<div class="placeholder">
Placeholder
</div>
</div>
<div class="images">
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/150x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/400x400"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
</div>

Categories