I've been trying to get my head around this probably very simple problem.
I have dynamically generated links, for example #l1, #l2.. and for each link I have a containing an image img. Divs have IDs corresponding to links, for example #li1, when clicked, should toggle div with id #di1 and so on.
I wrote a test, where I iterate through ID numbers and construct jquery button listeners. Something in the lines of:
a = [1,2,3,4]; // those are link and div IDs
for (k=0;k<3;k++){
$("#"+"li"+a[k]).click(function() {
$("#"+"di"+a[k]).toggle();
});
}
But what this gives me are listeners on all links, which toggle only the last div!
So again: I have links within a tag, which when clicked, should toggle a DIFFERENT div with corresponding ID.
What am I doing wrong here?
Thanks..
By the time click is invoked, value of k is 3(because of k++), just use this context.
Make sure for-loop should have k < 4 condition to iterate 4(0, 1, 2, 3) elements.
var a = [1, 2, 3, 4];
for (k = 0; k < 4; k++) {
$("#li" + a[k]).click(function() {
$(this).toggle();
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="li1">li1</div>
<div id="li2">li2</div>
<div id="li3">li3</div>
<div id="li4">li4</div>
Going with your approach:
Use closure, inner function remembers the environment in which it is created!
var a = [1, 2, 3, 4];
for (k = 0; k < 4; k++) {
$("#di" + a[k]).click((function(k) {
return function() {
$("#li" + a[k]).toggle();
}
})(k));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="di1">Div 1</div>
<div id="di2">Div 2</div>
<div id="di3">Div 3</div>
<div id="di4">Div 4</div>
<hr>
<hr>
<div id="li1">Links 1</div>
<div id="li2">Links 2</div>
<div id="li3">Links 3</div>
<div id="li4">Links 4</div>
You can also use forEach() as well for cleaner JS code (IMO):
// link and div IDs
var a = [1,2,3,4];
a.forEach(function (v)
{
$("#li" + v).click(function()
{
$("#di" + v).toggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id="li1">one</li>
<li id="li2">two</li>
<li id="li3">three</li>
<li id="li4">four</li>
</ul>
<div id="di1">
<p>One</p>
</div>
<div id="di2">
<p>Two</p>
</div>
<div id="di3">
<p>Three</p>
</div>
<div id="di4">
<p>Four</p>
</div>
Related
I am trying to detect if the selected element is the first child with the given class with jQuery. In the example, you can see that it is the first child with class .item but the selector :first-of-type doesn't work for that because it is not the first div.
How can this be solved?
var selectedItem = $('.list').find('.item.selected');
var isFirst = selectedItem.is(':first-of-type');
console.log('Is item first? ' + isFirst);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list">
<div class="search"></div>
<div class="item selected">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
we can use is method to check if two elements are the same or not.
so, a solution would have two variables, one is the currently selected element and one is the first element.
let selectedItem = $('.list').find('.item.selected');
let firstElement = $('.list').find('.item:first');
console.log($(selectedItem).is(firstElement)); // this returns true, false
jsfiddle
Using jquery's .index() overload you can apply it to a predefined collection (jquery object):
var isFirst = $(".list .item").index($('.list .item.selected')) == 0;
Updated snippet:
var items = $(".list .item");
var selectedItem = $('.list .item.selected');
var isFirst = items.index(selectedItem) == 0;
console.log(isFirst)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list">
<div class="search"></div>
<div class="item selected">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
I'm need to count specific divs and after each X divs insert another div with a paragraph in it. All in pure JavaScript.
I am done with counting the specific divs but I don't know how to handle the loop and insert the container and the paragraph.
<div class="main">
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
</div>
<script>
count = document.getElementsByClassName("special-div").length; // Output: X
</script>
A basic for statement would be suitable for this problem.
Use querySelectorAll instead of getElementsByClassName as it's easier to manage - QSA returns an array-like static node list whereas gEBCN returns a live HTML collection which may trip you up when you start inserting new elements during its iteration.
To find out where you should add the new div element use the remainder operator (also called "modulo"). As you iterate over the node list check to see if dividing the current index + 1 (arrays are zero-based) by the specified number results in zero. If it does, add in the new div element after the current one.
Note: I've separated out the code to create a new div element into a function to keep things simple.
const divs = document.querySelectorAll('.special-div');
// Loop over the array-like structure that
// qsa returns. Find the remainder of dividing
// the index by the supplied `n` argument. If it's
// zero create a new div element and add it `after`
// the current div
function insertAfter(n, text) {
for (let i = 0; i < divs.length; i++) {
if ((i + 1) % n === 0) {
const div = createDiv(text);
divs[i].after(div);
}
}
}
// For convenience a function that creates
// a new paragragh element wrapped in a div.
// It uses the text passed in as an argument
function createDiv(text) {
const div = document.createElement('div');
div.className = 'special-div';
const para = document.createElement('p');
para.textContent = 'new!';
div.appendChild(para);
return div;
}
// Call the main function. The arguments are
// the count where you add a new div, and the
// text you want to appear in the paragraph
insertAfter(2, 'new!');
div.special-div p { color: red; }
<div class="main">
<div class="special-div">one</div>
<div class="special-div">two</div>
<div class="special-div">three</div>
<div class="special-div">four</div>
<div class="special-div">five</div>
<div class="special-div">six</div>
</div>
This iterates the nodes with the special-div class, keeping track of the index. For non-zero indexes divisible by interval it creates and inserts a new div.
const interval = 3;
const parent = document.getElementById("parent");
const elements = [...document.getElementsByClassName("special-div")];
const divWithText = text => {
const newNode = document.createElement("div");
newNode.appendChild(document.createTextNode(text));
return newNode
};
elements.forEach((node,i) => {
if (i && i % interval === 0) {
parent.insertBefore(divWithText("new div"), node);
}
});
<div id="parent" class="main">
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
</div>
Sorry, I'm not quite sure what output you are expecting is like.
How about using .insertAdjacentHTML(<position>, <text>)? It inserts HTML code to a specified element. You don't need to count divs :)
element.insertAdjacentHTML()
You can choose a target element by changing element like main in my code.
position: 'beforeend'
This means "Just inside the element, after its last child."
In the case of Example 1, right before </div> of .main
position: 'afterend'
This means "After the element. Only valid if the element is in the DOM tree and has a parent element."
In the case of Example 2, right after </div> of .special-div
Element.insertAdjacentHTML() #MDN
Example 1
const main = document.querySelector('.main');
let html = `
<div>
<p>new</p>
</div>
`
// Insert html at the end of all children of .main
main.insertAdjacentHTML('beforeend', html);
<div class="main">
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
<div class="special-div">div 1</div>
</div>
Example 2
const targetDivs = document.querySelectorAll('.special-div');
let html = `
<div>
<p>new</p>
</div>
`
// Insert html after every specific div
targetDivs.forEach(div => div.insertAdjacentHTML('afterend', html));
// you can't do this: targetDivs.insertAdjacent()
// Because targetDivs is a NodeList which is like an array [].
// You have to use .insertAdjacent() on every div one by one,
// not on an array(NodeList)
<div class="main">
<div class="special-div">div 1</div>
<hr>
<div>Ignore this div</div>
<hr>
<div class="special-div">div 1</div>
<hr>
<div class="special-div">div 1</div>
</div>
You can try it like this:
var divs = document.getElementsByClassName('special-divs');
var afterHowMany= 5;
for( var i = 0; i < divs.length; i+=afterHowMany )
divs.slice(i,i+afterHowMany).wrapAll('<div class="inserted-div"></div>');
This recursively adds a new div after every 5 elements. You can take any other number apart from 5 as well.
I create some elements with local storage and it works fine but I want that items also should be cloned to a specific div tag.
here is my jsFidddle Code jsFiddle Demo
Now When I try to clone all element to <div class="all-items"></div> but it didn't work
here is my code below
$(function() {
$('.mix').click(function() {
$(this).toggleClass('selected')
window.localStorage.setItem('test' + this.dataset.id, $(this).hasClass('selected'));
});
$('.mix').each(function() {
var id = 'test' + this.dataset.id;
if (localStorage.getItem(id) && localStorage.getItem(id) == "true") {
$(this).addClass('selected');
}
});
$(document).ready(function() {
var e = $('.top-items');
for (var i = 0; i < 5; i++) {
e.clone().insertAfter(e);
}
});
and the HTML is here
<div class="top-items">
<div data-id="1" class="box p001 mix ">Div 1</div>
<div data-id="2" class="box p002 mix">Div 2</div>
<div data-id="3" class="box p002 mix">Div 2</div>
<div data-id="4" class="box p002 mix">Div 2</div>
<div data-id="5" class="box p002 mix">Div 2</div>
</div>
<div class="all-items"></div> //all elements should be clone here on click one by one
To achieve this i try on click function but it didn't give perfect solution so that i want when elements added they should be remove onclick from this <div class="all-items"></div> cloned tag.
any help or advice is highly appreciated.
You can do something like this
let topItemsHTML = $(".top-items").html()
$(".top-items").html(""); // clear top-items div
$(".all-items").html(topItemsHTML) // fill all-items with top-items
I have HTML elements iterated using a javascript loop
The html looks like this
<div class="description">Item 1</div>
<div class="description">Item 2</div>
<div class="description">Item 2</div>
The Javascript
let allItems = document.querySelectorAll(".description")
for (let i = 0; i <= allItems.length; i++) {
allItems[i].addEventListener("click", toggleSpinner.bind(this));
}
toggleSpinner = () => {
alert("I clicked") // here should be, for example, "I clicked Item 1,
// Or Item 2 or Item 3 depending on which was clicked
},
How do I call the function independently when each of the element is clicked
You could use something like this maybe?
toggleSpinner = (data) => {
console.log(`You clicked item ${data}`) // here should be, for example, "I clicked Item 1,
// Or Item 2 or Item 3 depending on which was clicked
}
let allItems = document.querySelectorAll(".description")
for (let i = 0; i < allItems.length; i++) {
allItems[i].addEventListener("click", toggleSpinner.bind(this, allItems[i].dataset.item));
}
<div class="description" data-item="1">Item 1</div>
<div class="description" data-item="2">Item 2</div>
<div class="description" data-item="3">Item 3</div>
let allItems = document.querySelectorAll(".description")
for (let i = 0; i <= allItems.length; i++) {
allItems[i].addEventListener("click", () => {toggleSpinner(allItems[i].innerHTML)});
}
toggleSpinner = content => {
alert(I clicked ${content})
},
Something like that. Add a parameter to the function and define what you want to add as this content in the loop
Are you looking something like this?
$(document).ready(function(){
$('div > *').click(function(){
alert('hi');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div>
<p> This is a paragraph </p>
<section> This is a section </section>
<span> This is a span </span>
<article> This is an article </article>
</div>
document.getElementById("getClickedElement").addEventListener("click", function(event) {
console.log("Clicked element is:- ", event.target.innerHTML);
});
<div id="getClickedElement">
<div class="description">Item 1</div>
<div class="description">Item 2</div>
<div class="description">Item 3</div>
</div>
Note:- Simply You have to wrap all the elements inside the parent div & bind addEventListener with that element. you will get targeted element data.
I am trying to loop over elements with a specific class and attach an ID to them.
var items = document.getElementsByClassName("item");
for(var i = 0; i < items.length; i++)
{
items[i].id = "item_" + i;
}
console.log(items);
If I run that I get the error Cannot set property 'id' of undefined
However the console.log(items) returns me the correct collection of items:
HTMLCollection []
0: div.item
1: div.item
length: 2
__proto__: HTMLCollection
But as soon as I try to get the index console.log(testimonials[0]) it is undefined.
HTML:
<div class="slider">
<div class="item">
Item 1
</div>
</div>
<div class="slider">
<div class="item">
Item 2
</div>
</div>
The issue could be your script is running before the DOM is fully ready.
To solve the issue, you can place your code at the bottom of the body.
OR:
Try wrapping your code with DOMContentLoaded, this will ensure that your code will be executed once the DOM is fully ready.
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var items = document.getElementsByClassName("item");
for(var i = 0; i < items.length; i++)
{
items[i].id = "item_" + i;
}
console.log(items);
});
</script>
<div class="slider">
<div class="item">
Item 1
</div>
</div>
<div class="slider">
<div class="item">
Item 2
</div>
</div>
With vanilla js always do DOM related operations after DOM is ready to use. And also before accessing/working with any DOM element check that it isn't nullable (not equal to null). With these practices you won't see any error with DOM elements because it is the safe way with manipulating with DOM elements. In regular loop always cache array.length. Avoid using anonymous function it is non-future proof and not debug-friendly way. Also write all js in separate js file.
HTML
<div class="slider">
<div class="item">
Item 1
</div>
</div>
<div class="slider">
<div class="item">
Item 2
</div>
</div>
JS
document.addEventListener("DOMContentLoaded", onDomReadyHandler);
function onDomReadyHandler(event) {
var items = document.getElementsByClassName('item');
var itemsLen = items.length;
for(var i = 0; i < itemsLen; i++) {
items[i].id = "item_" + i;
}
console.log(items);
}
Check this code:
<html>
<head>
<script>
window.onload = function(e){
var allItem = document.getElementsByClassName("item");
for(var i = 0; i < allItem.length; i++)
{
document.getElementsByClassName("item")[i].setAttribute("id", "item_" + i);
}
};
</script>
</head>
<body>
<div class="slider">
<div class="item" id="">
Item 1
</div>
</div>
<div class="slider">
<div class="item" id="">
Item 2
</div>
</div>
</body>
</html>
Hope it helps you.
Try using
items[i].setAttribute("id", "item_" + i);
Fore more details you can visit documentation of
https://www.w3schools.com/jsref/met_element_setattribute.asp
https://www.w3schools.com/jsref/met_element_getattribute.asp
Fiddle for it:
https://jsfiddle.net/abdulrauf618/n14v58zs