I have a javascript object which i am instantiating for each instance of a dom object. My code looks something like this
let div = null;
$(function() {
div = {
init: function(container) {
this.loadDom(container);
this.loadEvents();
},
loadDom:function(container) {
this.$container = $(container);
this.$buttons = this.$container.find('.button');
this.$textPanel = $('.text-panel')
},
loadEvents: function() {
let that = this;
// output the item's id. This works correctly
that.$textPanel.append(that.$container.attr('id') + '<br>');
this.$buttons.on('click', function() {
// output the item's id. This always outputs the last item
that.$textPanel.append(that.$container.attr('id') + '<br>');
});
}
};
let divs = $('.item');
if(divs.length > 0) {
divs.each(function(){
div.init(this);
})
}
});
here is a fiddle
I expect there to be one object created for each div with a class of 'item', and all the functions within that object to apply to that one div. i.e. when you click the red div, the container's id should be shown in the panel below.
In the loadEvents function, I list the id of the current div. This is run immediately and correctly lists "modal-1" and "modal-2". But when I run the same command after a button click, the id of the last div is always displayed rather than the current div.
How can I make the button click work so the id of the correct div is shown?
Thanks
I have reviewed the code by making div a function instead, so that the scope of each div will be unique and the events registered will belong to the div itself.
Other than that, the variable that was implicitly global, so I've added let before it so that it's correctly scoped.
It now works as intended
let div = null;
$(function() {
div = function(){
return {
init: function(container) {
this.loadDom(container);
this.loadEvents();
},
loadDom:function(container) {
this.$container = $(container);
console.log('con')
this.$buttons = this.$container.find('.button');
this.$textPanel = $('.text-panel')
},
loadEvents: function() {
let that = this;
// output the item's id. This works correctly
that.$textPanel.append(that.$container.attr('id') + '<br>');
this.$buttons.on('click', function() {
// output the item's id. This always outputs the last item
that.$textPanel.append(that.$container.attr('id') + '<br>');
});
}
}
}
let divs = $('.item');
if(divs.length > 0) {
divs.each(function(){
const _d = new div();
_d.init(this);
})
}
})
.container {
display: grid;
grid-gap: 30px;
grid-template-columns: 1fr 1fr;
}
.item {
border: 1px dotted green;
height: 100px;
width: 100%;
display: inline-block;
text-align: center;
}
.button {
height: 50px;
width: 50px;
background: red;
cursor: pointer
}
.text-panel {
border: 1px dotted black;
height: 200px;
grid-column: 1/3;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="item" id="modal-1">
<div class="button">click me</div>
modal-1
</div>
<div class="item" id="modal-2">
<div class="button">click me</div>
modal-2
</div>
<div class="text-panel"></div>
</div>
Related
I have one outer box and an inner box and there are some identical boxes with the same class names inside the second box. I want all of these boxes to appear one after the other with this fade-in effect. So far I have done it for the outer and inner div, and I wanted to use the same function for all the identical boxes inside. I tried to do the same for outer and inner div since they too just need the exact same function. But I wasn't successful. Here is my code :
html:
<div class="div1">
<div class="div2">
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
</div>
</div>
javascript:
let div1 = document.getElementsByClassName("div1")[0];
let div2 = document.getElementsByClassName("div2")[0];
let div3 = document.getElementsByClassName("div3");
div1.style.visibility = "hidden";
div2.style.visibility = "hidden";
function first() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
div1.style.animation = "fadein 5s";
div1.style.visibility = "visible";
resolve("div1 worked!");
}, 1000);
});
}
function second() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
div2.style.animation = "fadein 5s";
div2.style.visibility = "visible";
resolve("div2 worked!");
}, 1000);
});
}
function abc(element) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
element.style.animation = "fadein 5s";
element.style.visibility = "visible";
resolve("third");
}, 1000);
});
}
first()
.then(second)
.then((div3) => {
div3.forEach((element) => {
abc(element);
});
});
css
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
The first two works and I don't know how I can reuse the function for the remaining div3 class divs. I tried to reuse the function for the first two as well, but it didn't work and ended up writing same functions again and again. I want to call function abc for each element in div3 and only execute the next one after the first element is done - like how it executes for first and second but using the same function. Not sure how to do that and I'm stuck. Here is a codepen link. As of now all the div3 divs appear together with the second div.
You can use loops and animation-delay to apply the animation as per your need. The following code will work for this case. Code is full with comments to explain what is happening at each point. I have also slightly modified the css so that we don't get any weird blinking effect while executing the code.
//Declare all the classes -
let divs = ["div1", "div2", "div3"];
//Initiate a delay for each iteration
let delay = 0;
//Run a loop for each class
for(let i = 0; i<divs.length; i++){
//Get the element
let div = document.getElementsByClassName(divs[i]);
//Run a loop for element with the class
//(We only have one div with the classes div1 and div2. So it will run one time for them.
//We have 5 divs with div3 class. It will run 5 times in that case
for(let j = 0; j<div.length; j++){
//Get the individual element and add animation with delay
//The delay will also ensure that the animation starts only when the previous element has finished the animation
div[j].style.animation = `fadein 5s ${delay}s forwards` ;
div[j].classList.add("show");
//Increase delay with every iteration
delay+=5;
}
}
div {
visibility: hidden;
}
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
.show {
opacity: 0;
visibility: visible;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div class="div1">
<div class="div2">
<div class="div3">1</div>
<div class="div3">2</div>
<div class="div3">3</div>
<div class="div3">4</div>
<div class="div3">5</div>
</div>
</div>
The script may seem very long but it is only 10 lines long without the comments. This will also work if you increase or decrease the number of divs
This is one way to solve the problem using setInterval for executing a piece of code every x seconds.
The function fadeIn takes an array of elements that will be faded in by adding the class "show" (se details for "show" in CSS-code below). This will animate the given elements.
The function start collects the elements that will be faded and fades in the first collection of element, then countinuesly fades in the rest of the elements in the array "elements" every 3 seconds.
function fadeIn(el) {
// For every element in the collection
for (var i = 0; i < el.length; i++) {
// Add the class "show"
el[i].classList.add('show');
}
}
function start() {
/*
Collects all the elements that we want to fadeIn in order. First collection of elements will be animated firstly, second collection of elements will be animated secondly, etc.
*/
var elements = [];
elements.push(document.getElementsByClassName("div1"));
elements.push(document.getElementsByClassName("div2"));
elements.push(document.getElementsByClassName("div3"));
// Show the first collection of elements
fadeIn(elements[0]);
// Show the rest of the element collections in array "elements"
var i = 1;
fadeInInterval = setInterval(function() {
fadeIn(elements[i]);
// If there is no more collections to fade in, end the setInterval
if (i == elements.length-1) {
clearInterval(fadeInInterval)
}
i++
}, 3000) // Every 3 seconds
}
start();
div {
visibility: hidden;
}
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
.show {
animation: fadein 5s;
visibility: visible;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div class="div1">
<div class="div2">
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
</div>
</div>
I have created this application and right now when it runs i get the textbox value increasing with each click which i want.
the nested divs are also created with an image placed into the inner most div. how can i get that image to change between two images using the value from the textbox?
So if the value in the textbox is = an odd number because i clicked the button 1 time it shows image1.jpg and if the value in the textbox is = any even number is shows image2.jpg
I only need the nested divs to appear on the first click and have the image change with everyclick, using the vlue in the textbox to make that happen.
I was going to use a boolean but it has to use the value in the textbox. i know I have
not made that var global so maybe that is what i am having trouble with. idk if it goes into the addbutterfly function or the function used with the button. lots of help needed at the end of this application
var i = 0;
function runTogether1()
{
addDiv();
addDiv2();
addDiv3();
addbutterfly();
incrementValue();
}
function addDiv()
{
var div1 = document.createElement('div');
div1.classList.add('div1');
document.body.appendChild(div1);
}
function addDiv2()
{
var div2 = document.createElement('div');
div2.classList.add('div2');
document.getElementsByClassName("div1")[i].appendChild(div2);
}
function addDiv3()
{
var div3 = document.createElement('div');
div3.classList.add('div3');
document.getElementsByClassName("div2")[i].appendChild(div3);
}
function addbutterfly()
{
var img = document.createElement('img');
img.src = "bfly1.gif";
document.getElementsByClassName("div3")[i].appendChild(img);
i++;
}
function incrementValue()
{
var value = parseInt(document.getElementById('id1').value, 10);
value = isNaN(value) ? 0 : value;
value++;
document.getElementById('id1').value = value;
}
<html>
<head>
<meta charset="utf-8">
<script src ="bflyjs.js" defer></script>
</head>
<body>
<div class="button">
<button onclick = "runTogether1()">
click to get nested divs
</button>
<textarea id ="id1">
</textarea>
</div>
</body>
<style>
.div1 {
background-color: red;
margin: 1em;
height: 500px;
width: 500px;
justify-content: center;
align-items: center;
}
.div2 {
background-color: yellow;
height: 300px;
width: 300px;
}
.div3 {
background-color: limegreen;
height: 150px;
width: 150px;
}
div {
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
</style
</html>
The function addbutterfly() is the one responsible to add the image, so you can get the value of the textfield inside of it.
As you know the current value, it is possible to use it the way you want. In this case, it should look something like this:
function addbutterfly() {
var img = document.createElement('img');
// Get the value of the "textfield"
const value = parseInt(document.getElementById('id1').value, 10);
// If the value is even, add "bfly2.gif", otherwhise add "bfly1.gif"
img.src = value % 2 === 0 ? "bfly2.gif" : "bfly1.gif";
document.getElementsByClassName("div3")[i].appendChild(img);
i++;
}
I have a series of divs with the class name x that are inside of other divs with the class name item. The item divs are inside a section of the main.
What I want to do is create a function that applies equally to every x class div by affecting their respective parent (in this case changing their CSS).
I coded this:
var openbtn = document.getElementsByClassName("x")[0];
openbtn.onclick = function() {
document.body.parentElement.style.backgroundColor = "red";
}
However, this only works on the first x class <div>. When it works, it changes the background color of the section, or main or body element, and not the x class div parent (the item class div).
If you want to handle this with a handler on each .x element, you have to add a handler to each .x element. (But you may not want to do that, keep reading.) That would look like this:
var openbtns = document.getElementsByClassName("x");
for (var n = 0; n < openbtns.length; ++n) {
openbtns[n].addEventListener("click", xClickHandler);
}
...where xClickHandler uses this (or event.currentTarget) to know which .x element was clicked:
function xClickHandler() {
this.parentElement.style.backgroundColor = "red"; // I suggest using a class instead of doing this, btw
}
But, if all of these .x elements are within the same overall container, you can do it with event delegation, like this:
document.querySelector("selector-for-the-overall-container").addEventListener("click", function(event) {
// Find the `.x` that was clicked (which may be `event.target` or may be
// an ancestor node of it
var x = event.target.closest(".x");
if (x && this.contains(x)) {
x.parentElement.style.backgroundColor = "red"; // Again, suggest using a class
}
});
More:
closest
contains
Live Example using the HTML from your comment:
document.getElementById("items").addEventListener("click", function(event) {
// Find the `.x` that was clicked (which may be `event.target` or may be
// an ancestor node of it
var x = event.target.closest(".x");
if (x && this.contains(x)) {
x.parentElement.style.backgroundColor = "red"; // Again, suggest using a class
}
});
.x {
display: inline-block;
width: 10px;
height: 10px;
background-color: blue;
}
section {
background-color: yellow;
padding: 8px;
}
<main id="items">
<section id="design">
<div class="item">
<div class="x"></div>
<h1>Design stuff</h1>
</div>
</section>
<section id="art">
<div class="item">
<h1>Art stuff</h1>
<div class="x"></div>
</div>
</section>
</main>
Add the event listener to each element using a for of loop:
var openbtnList = document.getElementsByClassName("x");
for (let x of openbtnList) {
x.addEventListener("click", clickX, false);
};
function clickX() {
this.parentElement.classList.toggle("red"); // sstyle.backgroundColor = "red";
}
main {
display: flex;
}
.item {
width: 80px;
height: 100px;
padding: 30px;
}
.item::before {
content: " ";
background: url(http://placekitten.com/80/100) center/cover;
width: 80px;
height: 100px;
position: absolute;
}
.x::before {
content: "X";
color: white;
font: 30px/30px sans-serif;
padding: 10px;
position: absolute;
}
.red {
background: rgba(255, 0, 0, 0.5);
}
<main>
<div class="item">
<div class="x"></div>
</div>
<div class="item">
<div class="x"></div>
</div>
<div class="item">
<div class="x"></div>
</div>
</main>
To get a better idea what i'm doing look here for my previous code that i try to make a little better >>Codepen
I want to have an array that i fill up with all the id's that i try to animate and with one function toggle the classes .open .closed on every id in the array.
so on an click add .open to #Hamburger, #Navigation, #Black-filter. and one second click remove .open and add .closed for those id's.
because i'm still learning javascript i want it to work in vanilla javascript so i understand the basics before im going on with jquery.
var hamburger = document.getElementById('Hamburger');
var navigation = document.getElementById('Navigation');
var blackFilter = document.getElementById('Black-filter');
var isOpen = true; // true or false
var animation = [h, s, b]; // #H #S #B
var open = "open"; // .open
var closed = "closed"; // .closed
function trigger() {
if (isOpen === true) {
animation.classList.add(open); // add .open to all id's
animation.classList.remove(closed); // remove .closed from all id's
} else {
animation.classList.add(closed);
animation.classList.remove(open);
}
isOpen = !isOpen; // toggles true to false
}
hamburger.addEventListener('click', trigger, false); // onclick toggle class
blackFilter.addEventListener('click', trigger, false); // onclick toggle class
body {
width: 100%;
}
#Hamburger {
height: 100px;
background: red;
width: 100px;
}
#Hamburger.open {
opacity: 0.5;
}
#Hamburger.closed {
opacity: 1;
}
#Navigation {
height: 100px;
background: blue;
width: 100px;
}
#Navigation.open {
opacity: 0.5;
}
#Navigation.closed {
opacity: 1;
}
#Black-filter {
height: 100px;
background: green;
width: 100px;
}
#Black-filter.open {
opacity: 0.5;
}
#Black-filter.closed {
opacity: 1;
}
<body>
<div id="Hamburger"></div>
<div id="Navigation"></div>
<div id="Black-filter"></div>
</body>
What you are looking for is:
var isOpen = true;
var hamburger = document.getElementById('Hamburger');
var navigation = document.getElementById('Navigation');
var blackFilter = document.getElementById('Black-filter');
var animatable = [hamburger, navigation, blackFilter];
var openClass = "open"; // .open
var closedClass = "closed"; // .closed
function trigger() {
if (isOpen) {
animatable.forEach(function (element) {
element.classList.add(openClass);
element.classList.remove(closedClass);
});
} else {
animatable.forEach(function (element) {
element.classList.add(closedClass);
element.classList.remove(openClass);
});
}
isOpen = !isOpen;
}
hamburger.addEventListener('click', trigger, false);
blackFilter.addEventListener('click', trigger, false);
Demo
There are a few things that need improvement.
First of all you are naming you variables rather poorly. Which is actually already one of your problems, first you say that
var b = document.getElementById('B');
and then later
var b = "closed";
So this needs to be fixed, use variable names that are descriptive so you will know what you are talking about when.
Last but not least you are trying to change the elements of that array a, not the array itself. So you need to access the elements by themselves, set their classes and then you are good to go e.g.:
for( var index in a ) {
if ( open === true ) {
a[index].classList.add(b);
a[index].classList.remove(c);
} else {
a[index].classList.add(c);
a[index].classList.remove(b);
}
open = !open;
Firstly ou don't need "open" AND "close" classes, only one would clearly simplify your code (and there is the "default" state).
Then, add a class for all your buttons, the easily manipulate them in JS and CSS (here the class ".btn");
// Directly get on array (a NodeList more precisely)
var buttons = document.getElementsByClassName('btn');
function toggleClass() {
// Loop to add or remove (toggle) the the '.open' class
for (var i=0; i<buttons.length; i++) {
buttons[i].classList.toggle('open');
}
}
// Loop to add event listener to all buttons
for (var i=0; i<buttons.length; i++) {
buttons[i].addEventListener('click', toggleClass, false);
}
.btn {
height: 100px;
width: 100px;
}
.btn.open {
opacity: 0.5;
}
#Hamburger { background: red; }
#Navigation { background: blue; }
#Black-filter { background: green; }
<div id="Hamburger" class="btn"></div>
<div id="Navigation" class="btn"></div>
<div id="Black-filter" class="btn"></div>
This is already way simpler. But you should have a parent element holding the opened/closes state, so you wouldn't loop in an array.
// Only need to manipulate one DOM node
var menu = document.getElementById('menu');
function toggleClass() {
menu.classList.toggle('open');
}
menu.addEventListener('click', toggleClass, false);
body {
width: 100%;
}
.btn {
height: 100px;
width: 100px;
}
.menu.open > .btn {
opacity: 0.5;
}
#Hamburger { background: red; }
#Navigation { background: blue; }
#Black-filter { background: green; }
<div class="menu" id="menu">
<div id="Hamburger" class="btn"></div>
<div id="Navigation" class="btn"></div>
<div id="Black-filter" class="btn"
</div>
Your event listener gets the event as the 1st argument. Use it to decide what to do:
function trigger(event) {// use event.target ... }
I am trying to, sort of, emulate the effect here. Essentially, during scrolling, change the css (drop shadow), and when the element comes back to original position (remove shadow).
I am able to detect scroll, but not able to figure out how to detect the return to the original un-scrolled state.
HTML
<div id="container">
<ul>
<li id="one">el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li><li>el</li>
</ul>
</div>
CSS
html, body {
margin: 0;
padding: 0;
height: 100%;
}
#container {
height: 100px;
width: 500px;
border: 1px solid #000000;
overflow: scroll;
}
JS (with jquery)
var p = $('#one');
var position0 = p.position().top;
$('#container').scroll(function () {
if (p.position().top != position0) {
console.log('p.position: ' + p.position().top);
$('#container').css('background-color', 'pink');
}
});
JSFIDDLE: http://jsfiddle.net/nrao89m3/
PS: From console.log it doesn't seem to return to its original value at all.
Just add an else block:
var p = $('#one');
var position0 = p.position().top;
$('#container').scroll(function () {
if (p.position().top != position0) {
console.log('p.position: ' + p.position().top);
$('#container').css('background-color', 'pink');
} else {
$('#container').css('background-color', 'white');
}
});
http://jsfiddle.net/vyjbwne2/