I am trying to use an array index to allow a set of div IDs to change from one ID to another when the mouseenter and mouseleave functions are triggered.
I know there are other ways to do this - toggle, hover, or CSS hover. This is just learning for me, and I am very new.
The code below is commented, and the basic problem is related to why an array variable of "largeID" (or smallID) outputs the proper values, but trying to use an index value doesn't. For each for statement, I want the smallID[i] value to be replaced with the largeID[i] value when the mouse enters the div, but I don't want to write the code for each one, i.e. "largeID[1], largeID[2].
Thanks for any pointers!!
$(document).ready(function() {
var smallID = [];
var largeID = [];
var divList = document.getElementsByTagName('div')[1]; //get the second (radialMenu) div in the document
var radialDivList = divList.getElementsByTagName('div'); // get all divs under the second (radialMenu) div
for(var i = 0; i < radialDivList.length; i++) {
if (!radialDivList[i]) continue; //skip null, undefined and non-existent elements
if (!radialDivList.hasOwnProperty(i)) continue; //skip inherited properties
smallID[i] = radialDivList[i].id; //assign the list of four divs to the smallID array;
largeID[i] = smallID[i] + 'Full'; // add "Full" to each smallID element to make a largeID element
alert(smallID[i]); // alerts for smallID / largeID and smallID[i] / largeID[i]
alert(largeID[i]); // give rational and expected output
$('#' + smallID[i]).mouseenter(function () { //works for all four radial menu divs when mouse enters
//BUT largeID[i] is undefined
alert(largeID[i]); // undefined
alert(largeID); // gives expected output of full array
}).mouseleave(function () { //mouseleave function not working
});
}
The reason your largeID[i] is undefined in your mouseenter function is because the last value of i is remembered and used on your mouseenter events.
When you use a variable and it goes "out of scope" a closure is automatically created to allow the variable to still exist for the function that still needs it, and your mouseenter functions all reference the same variable.
Your for loop stops when i is more than the amount of divs you have using radialDivList.length. Every attempt to use i to reference an index in your array will now be out of bounds.
The first answer on this page explains it well:
JavaScript closure inside loops – simple practical example
I've modified your example to create a new function with it's own copy of "i". So when hovering over the first div i will equal 0, when hovering over the second div it will equal 1, etc.
$(document).ready(function() {
var smallID = [];
var largeID = [];
var divList = document.getElementsByTagName('div')[1]; //get the second (radialMenu) div in the document
var radialDivList = divList.getElementsByTagName('div'); // get all divs under the second (radialMenu) div
var funcs = [];
for (var i = 0; i < radialDivList.length; i++) {
if (!radialDivList[i]) continue; //skip null, undefined and non-existent elements
if (!radialDivList.hasOwnProperty(i)) continue; //skip inherited properties
smallID[i] = radialDivList[i].id; //assign the list of four divs to the smallID array;
largeID[i] = smallID[i] + 'Full'; // add "Full" to each smallID element to make a largeID element
alert(smallID[i]); // alerts for smallID / largeID and smallID[i] / largeID[i]
alert(largeID[i]); // give rational and expected output
funcs[i] = function(i) {
$('#' + smallID[i]).mouseenter(function() { //works for all four radial menu divs when mouse enters
//BUT largeID[i] is undefined
alert(largeID[i]); // undefined
alert(largeID); // gives expected output of full array
}).mouseleave(function() { //mouseleave function not working
});
}.bind(this, i);
}
for (var i = 0; i < funcs.length; i++) {
funcs[i]();
}
});
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<title>Example</title>
</head>
<body>
<div>
<div>
<div id="one" style="background:green;height:40px">
</div>
<div id="two" style="background:red;height:40px">
</div>
<div id="three" style="background:blue;height:40px">
</div>
</div>
</div>
</body>
</html>
Related
I have a few elements on my webpage that share the class name "table-data". I am trying to loop through them and give each one of them a unique id, then I want all them to have the same onclick attribute. I was able to successfully give each one of them a unique id with pure JavaScript, but the part that I am having trouble with is giving them all the same onclick attribute. I tried with JavaScript and jQuery and failed using both.
Here is my code:
// this counter will be used to give each element a unique id
var counter = 0;
// x holds all elements with the class name "table-data"
var x = document.getElementsByClassName("table-data");
// loop through all the elements in the array x
for (var i = 0; i < x.length; i++) {
// give each element the name "idNum" plus whatever number the counter variable holds
var idName = 'idNum' + String(counter);
// set the id name to the variable defined above
x[i].setAttribute('id', idName);
// here is the part I am struggling with, this section is supposed to alert the id of the element when it is clicked
$('#' + idName).click(function () {
alert(idName);
});
// increment the counter
counter += 1;
The problem I am having is that all elements are showing the idName of the last element in the array "x". so if there are 5 elements in total and I run this code on them, they will all show the idName of the 5th element when clicked. I tried to alert() the idName outside of the onclick function and make it so the loop will alert the idName of each element as soon as it is assigned and that worked just fine. I would get five different ids for five different elements, so what is causing the problem inside the onclick function?
I also tried the following and it didn't work either:
x[i].setAttribute('onclick', 'call_alert(this.idName)');
function call_alert(clickedId) {
alert(clickedId);
}
I am very new to web programming so I am sorry if there are any obvious mistakes in my code.
You're making this much more complicated than your need to. You don't need to add an id to every element, you can simply bind on a class using the class(.) selector and then add the click binding all in one go:
$(document).ready(function() {
//match on the class
$('.table-data').click(function(){
//access the actual element clicked using $(this)
alert('clicked'+ $(this).text());
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
1
2
You won't need counter variable as you're already i:
//var counter = 0;
var x = document.getElementsByClassName("table-data");
for (var i = 0; i < x.length; i++) {
var idName = 'idNum' + String(i);
x[i].setAttribute('id', idName);
x[i].onclick = function() {
console.log(this.id);
}
//counter += 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="table-data">table1</div>
<div class="table-data">table2</div>
<div class="table-data">table3</div>
<div class="table-data">table4</div>
<div class="table-data">table5</div>
<div class="table-data">table6</div>
jQuery version:
$(".table-data").each((idx, element) => {
$(element).attr("id", "idNum" + String(idx));
$(element).on("click", () => {
console.log($(element).attr("id"));
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="table-data">table1</div>
<div class="table-data">table2</div>
<div class="table-data">table3</div>
<div class="table-data">table4</div>
<div class="table-data">table5</div>
<div class="table-data">table6</div>
I'm creating 5 objects, each one with a div attached as source attribute, at which onclick should open the relative html page in an iframe.
Need to access the simple value of i==[i].num for each element on onclick function, so to select actor[i].num from within actor[i].source, thought it should be simple.
// html
<div id="op0" class="op"><img src="gfx/0.svg"></div>
<div id="op1" class="op"><img src="gfx/1.svg"></div>
<div id="op2" class="op"><img src="gfx/2.svg"></div>
<div id="op3" class="op"><img src="gfx/3.svg"></div>
<div id="op4" class="op"><img src="gfx/4.svg"></div>
// js
var i=0;
let actor = {};
for (i = 0; i < 5; i++) {
actor[i] = {
num: i,
source: document.getElementById('op'+[i]),
clicked: 0
}
actor[i].source.addEventListener( 'click', function() {
console.log(this) // the source attribute <div>
console.log(this.num) // undefined
console.log(parent.num) // undefined
console.log(actor[i].num) // TypeError
clickActor(i) // returns 5 at the time of click
});
}
function clickActor(num){
if (actor[num].open == 0){
actor[num].source.classList.add("scaleUp");
document.getElementById("iFrame").src = "op"+num+".html";
actor[num].open = 1;
// div to move and scale
} else {
actor[num].source.classList.remove("scaleUp");
document.getElementById("iFrame").src = "";
document.getElementById("iFrame").style.pointerEvents = "none";
actor[num].open = 0;
// div to move and scale back
}
}
var variables are function scopes, so your i variable is being reused over and over again in your loop, so that when you actually click the button, i will be the last number of the loop. You can bypass this by simply creating a new function scope by inserting a self-executing function in place of your callback function.
actor[i].source.addEventListener( 'click', (function(i) {
return (function() {
console.log(actor[i].num) // this is should print out the object assign above it
clickActor(i) // i is now the index of the actor
});
})(i));
I'm having a little trouble adding an event listener to elements created dynamically.
I have a list of objects that represent matches a player plays in. And a for loop that iterates through them, creating 4 buttons for each match, a +1, a +2, a -1 and an END button.
However, even if they are well created, and appended where they should be, the event listeners are not kept. Here's a sample of my code:
var container = document.getElementById('container');
for (var i = 0; i < matches.length; i++) {
var row = document.createElement("div");
row.className = "row";
var plusOne = document.createElement("a");
plusOne.id = "plusone_ " + matches[i].id;
plusOne.addEventListener('click', function () {
alert('Clicked on plusOne!')
});
...
// adding plus two, minus one and END the same way
// however, END does have an eventListener on click
row.appendChild(plusOne);
container.appendChild(row);
}
If anyone has any idea why?
I have a set of two divs - First set: when people mouse over these divs, it will fire an event, Second set: when the event is fired, these divs will be displayed.
When you mouse over a div in the first set, it should display its corresponding div in the second set. I thought an easy way to match the mouseover divs with the correct div to display would be using arrays. I've been able attach the event listeners properly, but I can't figure out how to set it up so that when you mouseover one object of an array, it displays the array object with the same index number. I think if I could figure out how to recoginze the index number of the object I am mousing over, I could get it to work. I've tried a lot of things, but haven't been able to create anything that works. Here's the code:
<script>
$(document).ready(function(){
//create array of divs to mouse over
var ar = new Array();
ar[0] = $("#home");
ar[1] = $("#doc");
var length = ar.length;
//create array of divs to display when event is fired
var des = new Array();
des[0] = $("#homeDes");
des[1] = $("#docDes");
// start for
for ( var i = 0; i< length; ++i )
{
ar[i].bind("mouseover",function(){$(des[i]).css("display","block");});
ar[i].bind("mouseout",function(){$(des[i]).css("display","none");});
}
//end for
});
//end
</script>
I would tend toward making a more flexible approach to this so that you don't need to change your javascript when you change your HTML. Consider classing your elements that need to have the bindings and providing data attribute to specify the target. Your HTML for divs to be bound might look like this:
<div id="home" class="mouseoverToggleTrigger" data-target="#homeDes">...</div>
And the jQuery might look like this:
$(document).ready(function(){
$('.mouseoverToggleTrigger').hover(function() {
var $target = $($(this).data('target'));
$target.toggle();
}
});
Note this is assuming you are using HTML5 for which jQuery, by default, converts data-* into values retrievable via data().
For pages that are not HTML5, this more generalized solution will work
$(document).ready(function(){
$('.mouseoverToggleTrigger').hover(function() {
var $target = $($(this).prop('data-target'));
$target.toggle();
}
});
One additional bit of flexibility this gives, is that you now don't have to limit yourself to a one-to-one trigger to target mapping. You could specify a class name or other jQuery selector for data-target values to get highly customized behavior, such as one trigger toggling all elements of a certain class that are children of another class.
$(document).ready(function(){
//create array of divs to mouse over
var ar = new Array();
ar[0] = $("#home");
ar[1] = $("#doc");
var length = ar.length;
//create array of divs to display when event is fired
var des = new Array();
des[0] = $("#homeDes");
des[1] = $("#docDes");
// start for
for ( var i = 0; i< length; ++i )
{
// WRAP THE BODY OF THE FOR LOOP IN A FUNCTION
function(index) {
ar[index].bind("mouseover",function() {
$(des[index]).css("display","block");}
);
ar[index].bind("mouseout",function() {
$(des[index]).css("display","none");
});
}(i);
}
//end for
});
When the events are fired the value of i is the length of the array, you have to pass the value of i to another function so that in each function scope the value of index will be the value of i when it was called.
A simpler approach code wise is to give the common elements common classes and then use jQuery index() and eq() to match pairings
HTML
<a id="home" class="hoverMe">
<a id="doc" class="hoverMe">
<div id="homeDes" class="content">
<div id="docDes" class="content">
JS
var $content=$('.content')
var $links=$('.hoverMe').hover(function(){
$content.eq( $links.index(this) ).show()
},function(){
$content.eq( $links.index(this) ).hide()
})
index() API Docs
eq() API Docs
I have created function for creating a div, when u selet the value in dropdown box , based upon the length the number of divs will be created , so the code is
<select onchange="test()" id="selected_opt">
<option value="0" selected>-Select-</option>
<option value="1">Communication</option>
<option value="2">XXXXXXXXXXXXX</option>
</select>
the function test is
function test(){
var result = get_id.options[get_id.selectedIndex].value;
if(result == 1){
var i = 0,
c = model_details_json.communication,
j = c.length,
communications_div = document.getElementById("model_communication");
if(j == 0){
alert('nothing');
}
for (; i<j; i++){
var communication = c[i];
var create_div = document.createElement('div');
create_div.id = 'communication_id'+i;
create_div.name = 'communication';
var create_anchor = document.createElement('a');
create_anchor.innerHTML = communication.communication_name;
communications_div.appendChild(create_div);
document.getElementById(create_div.id).appendChild(create_anchor);
create_anchor.setAttribute("href", "javascript:void(0);");
create_anchor.setAttribute("onclick","sample('"+communication.communication_name+"','"+create_div.name+"')");
}
}
for example the length is 6 means the six number of divs will be created , so what the problem is when i again click on communication in select dropdown i.e already the six divs have been created , when do it again then agin six divs are created , so totally 12 divs created when u do it again it goes for 6 multiples.......
so what i need is the number of div would not be repeated. and it should be validate whether the user is clicking the same value in dropdown
Check this to remove div elements considering your parent div model_communication.
You need to implement logic by checking if the div exist and stop the loop when you get a message like 'Div is already removed' as shown in the example.
In order to do so create div elements along with id
var newdiv = document.createElement('div');
newdiv.setAttribute('id', id);
You need to remove all divs before create the new ones. Try adding a class name to it:
var create_div = document.createElement('div');
create_div.className = 'communication_div';
...
Now you can select the divs. Before the for statement add these lines to remove the divs with 'communication_div' class name:
var divs = document.getElementsByClassName('communication_div');
for(var i=0; i<divs.length; i++) {
divs[i].parentNode.removeChild(divs[i]);
}
Every script run will generate new divs and remove old ones.
use js map object to put selected value or length as key into the map then everytime user clicks a value, first check for its existence in the map. If not found in the map, that would mean length is not repeating and divs will be created.
something like:
var selectedValues = new Array();
.......
var result = get_id.options[get_id.selectedIndex].value;
if(selectedValues["val_"+result]) {
return;
}
selectedValues["val_"+result] = true;
you can check if the div is already created or present on page using getElementById('elementId') before creating it.
like in you code
for (; i<j; i++){
if(! document.getElementById('communication_id'+i)){ // do if element is not present on page
var communication = c[i];
var create_div = document.createElement('div');
create_div.id = 'communication_id'+i;
create_div.name = 'communication';
var create_anchor = document.createElement('a');
create_anchor.innerHTML = communication.communication_name;
communications_div.appendChild(create_div);
document.getElementById(create_div.id).appendChild(create_anchor);
create_anchor.setAttribute("href", "javascript:void(0);");
create_anchor.setAttribute("onclick",
"sample('"+communication.communication_name+"','"+create_div.name+"')");
}
}
Use replaceChild() instead of appendChild() on the Element object.