I am facing a problem with JavaScript removeChild function and unfortunately its the first time I can't find what's wrong on the forum. Here is the scenario:
-Create div elements with a for loop and put the word test in it
-When one of those div is clicked, append to it the div element stored in a Foo object
-If the div from Foo object is clicked, remove it from DOM
Here is the code I use. Tried in IE and FF. No error is displayed, removeChild SEEMS to work ok, BUT the foo div is never removed. Hopefully someone can help me find out why that doesn't work.
function Foo() {
this.container = document.createElement("div");
this.container.appendChild(document.createTextNode("foo"));
this.container.onclick = function() {
// in the function'this' refers to the clicked element, i.e. container
console.log(this.parentNode); // the clicked div has a parent
console.log(this.parentNode.removeChild(this)); // the div is removed from DOM
console.log(this.parentNode); // null, the clicked div has no more parent but...
// foo is STILL DISPLAYED ?!
}
}
var foo = new Foo();
function Test() {
// create 3 div with the word test inside
for(i=0; i<3; i++) {
var t = document.body.appendChild(document.createElement("div"));
t.appendChild(document.createTextNode("test"));
// when test div is clicked, append to it the container (a div element) from object foo
// due to the for loop we need a closure for t
t.onclick = function(t){ return function() {
t.appendChild(foo.container); // here it works great and t is moved to the clicked test div
}; }(t);
}
}
new Test();
Many thanks for taking the time to investigate my issue
When you click the container with the word foo in it, you also click the t div with the word test in it. Which has a click handler that does append your foo.container back to the element from which your foo.container.onclick handler has just removed it.
Quick fix: After the container has handled the click event, it stops propagation of the event to elements higher in the DOM tree:
…
this.container.onclick = function(e) {
// in the function'this' refers to the clicked element, i.e. container
console.log(this.parentNode); // the clicked div has a parent
console.log(this.parentNode.removeChild(this)); // the div is removed from DOM
console.log(this.parentNode); // null, the clicked div has no more parent
e.stopPropagation();
// foo is NO MORE DISPLAYED
}
…
Advanced fix: Don't attach the handler that adds foo to the t element, but only to the test word. You will need an extra element for that, though:
…
var t = document.body.appendChild(document.createElement("div"));
var s = t.appendChild(document.createElement("span"));
s.appendChild(document.createTextNode("test"));
// when test div is clicked, append to it the container (a div element) from object foo
// due to the for loop we need a closure for t
s.onclick = function(s){ return function() {
s.appendChild(foo.container); // here it works great and t is moved to the clicked test div
}; }(s);
…
A variant of this would be to not append the foo.container to t itself, but as a sibling or so.
you added onclick event to the container, so the correct code would be:
function Foo() {
this.container = document.createElement("div");
this.container.appendChild(document.createTextNode("foofoofoooooo"));
this.container.onclick = function() {
this.removeChild(this.firstChild);
}
}
var foo = new Foo();
function Test() {
// create 3 div with the word test inside
for(i=0; i<3; i++) {
var t = document.body.appendChild(document.createElement("div"));
t.appendChild(document.createTextNode("test"));
// when test div is clicked, append to it the container (a div element) from object foo
// due to the for loop we need a closure for t
t.onclick = function(t){ return function() {
t.appendChild(foo.container); // here it works great and t is moved to the clicked test div
}; }(t);
}
}
new Test();
I tested and it works!
Related
I have a list of anchor in my html, I want to make their href editable.
Everything fine, but the validation step (last onclick) refers to the last anchor instead of the current one
var anchors = document.querySelectorAll('.home-content a');
var col = document.querySelectorAll('.home-content > article');
anchors.forEach((k)=> {
let linkpanel = document.getElementById('link-edit-panel'); //This element is a single div in my html
let linkpanelvalidate = document.getElementById('validate-link'); //the button inside the said div
let editinput = linkpanel.querySelector('input'); //the input inside this div
//For each anchors, I add a button that will let user show the "linkpanel" div to edit the href of this anchor
let editbut = document.createElement('div');
let linktxt = k.href;
editbut.classList.add('edit-but','toremove');
editbut.innerHTML = "<i class='fas fa-link'></i>";
//I put this new element to the current anchor
k.appendChild(editbut);
console.log(k); // this to show me the full list of anchors
/* PROBLEM START HERE */
//click on the "edit" button
editbut.onclick = ()=>{
console.log(k); //Here, it shows the good anchor!
}
//click on the "validate" button
linkpanelvalidate.onclick = ()=>{
console.log(k); //Here, it shows the very last anchor...
}
});
I tried to put the element inside a constant
const ttt = k;
It does not change a thing.
Thank you for your help
We are facing here a classical forEach bubbling misunderstand (and I was blind not to see it)
When the click on the validate button occures, the call is made from the "main" bubble (outside the loop function if you need to picture it) so naturaly, it returns the last occurrence of the loop when we print the value in the console for example.
Solution
There is many solutions, you can store these values in an array to use each of them later
var arr = [];
node.forEach((v)=>{
arr.push(v);
});
Or, you don't want to deal with an array and want to keep it simple, like me, and you create your button during the forEach loop event, like this
node.forEach((v)=>{
let btn = document.createElement('button');
document.body.appendChild(btn);
btn.onclick = ()=> {
console.log(v); //it is the current value, not the last one
//you can create another button here and put his onclick here, the value will still remains etc
}
});
I am aware I can use the click function to attach an event to the DIV element but for some reason it is not working for me. Here is how I am creating the DIV element.
function createColorSwatchDiv(color) {
var colorSwatchDiv = $("<div>");
var image = $("<img>");
image.attr("src",color.imageURL);
var label = $("<label>");
label.text(color.title);
colorSwatchDiv.append(image);
return colorSwatchDiv;
}
Then I try to attach the click event like the following:
// run a loop and build the grid layout
for(index = 0; index < colors.length; index++) {
var colorSwatchDiv = createColorSwatchDiv(colors[index]);
// attach the event
colorSwatchDiv.click(function(){
alert('hello world');
});
colorsSection.append(colorSwatchDiv);
}
// add to the dom
$("#color .imageChartOption").after(colorsSection);
But it does not work and no click event is been attached.
following is the code
var $newdiv1 = $("<div id='object1' onClick=Test()>Hello</div>");
$("body").append($newdiv1);
function Test()
{
alert("Clicked");
}
OR
$newdiv1.on('click',function(){alert("hello");});
since you have created the div in a jQuery wrapper you don't need to wrap it again here $(colorSwatchDiv).click(.... Also, are you sure that the colorSwatchDiv variable is referencing the dom element and not the in memory element? Can you apply a class or anything to the elm in the dom?
I have this function that create a new div with a class name.
Now I want to add a new div inside the created div. But nothing happened. When I look in the console with Chrome, nothing gets rendered inside the new div...
Any ideas?
function diceWrapper(){
var wrappId=document.createElement("div");
document.getElementById("page-content-wrapper").appendChild(wrappId);
document.getElementsByTagName("div")[3].setAttribute("class", "dice-window-wrapper");
}
function menubar(){
var menuid=document.createElement("div");
document.getElementById("dice-window-wrapper").appendChild(menuid);
document.getElementsByTagName("div")[4].setAttribute("class", "dice-menubar-wrapper");
}
And while I'm already asking, the new created <div class="dice-window-wrapper".
Keeps pushing down some other elements that are suposed to be before this div class. Even when the attributes are [0] and [1].
It seems like you're trying to find the newly created <div> and then modify it. It would be better to make all modifications first and only then add it to the document.
function createElementWithClass(elementName, className)
{
var el = document.createElement(elementName);
el.className = className;
return el;
}
var outerDiv = createElementWithClass('div', 'dice-window-wrapper'),
innerDiv = createElementWithClass('div', 'dice-menubar-wrapper');
outerDiv.appendChild(innerDiv);
document
.getElementById("page-content-wrapper")
.appendChild(outerDiv);
I need to set events to elements makes "on the fly", like var X = $('HTML CODE HERE'), but when I set the events to the last element, all other elements get this last event.
Example here: http://jsfiddle.net/QmxX4/6/
$(document).ready(function() {
var ulItem = $('.lst');
for (var x=0; x<5; x++) {
var newItemElement = $('<li style="border:solid 1px blue;width:200px;height:40px;"></li>');
ulItem.append(newItemElement);
var generator = Math.random();
newItemElement.on('click', function() {
console.log(generator);
});
}
});
All elements are diferents, and I attach the event in the element directly, im try to append before and after add event to element, but not work, all element get the last event.
If you make click in the <li></li> get code generated in the last event, but "in theory" all elements have diferent events attached..
But if I make other loop appending elements after append al items to <ul></ul>, like this:
$.each($(ulItem).children('li'), function(i, item) {
console.log($(this));
var generator = Math.random();
$(this).on('click', function() {
console.log(generator);
});
});
Works... what is the problem?
In your first loop, the generator variable belongs to the ready callback function, and the inner log functions all share it.
In your second loop, the generator variable belongs to the each callback function which is called once for each item and therefore the log functions all see a different variable.
O.K. Maybe I have asked something similar to this, but this is another problem I have. I will start by showing the code:
var MyClass = {
// Set up the events for the parent
// MyParent is a DIV
SetEvents: function() {
MyParent.onmouseover = function() { MyClass.MouseHover(MyParent); };
MyParent.onmouseout = function() { MyClass.MouseOut(MyParent); };
},
// Function activated when moving the mouse over the parent
MouseHover: function(Parent) {
if (Parent) {
// Here I create the child, when moving the mouse over the parent control
// The child is an IMG
var child = document.createElement("img");
child.id = "child";
child.src = "child.png";
child.style.float = "right";
Parent.appendChild(child);
}
},
// Function activated when moving the mouse out of the parent control
MouseOut: function(Parent) {
if (Parent) {
var child = document.getElementById("child");
if (child) {
// On mouse out I remove the child
Parent.removeChild(child);
}
}
}
}
So, this is just about a simple image appearing when I move the mouse over a div, then disappears when I move the mouse out. Yet, this is not behaving as I want.
The problem is that when, while the mouse is over the parent div, if I move it over the child img (still over the parent), the MouseOut() function is fired.
I have been searching for solutions and tricks all these days but everything was in vain. Now, I hope somebody can identify the problem. There must be some JavaScript chapter that I missed out. Thanks to everybody!
I added some images, to explain this better
UPDATES
Looks like this happens only when the IMG child is created dinamically. If I create it before and only change it's src, it works.
JSSFIDLE DEMO
You can see this script working (well, not working, better said) at http://jsfiddle.net/E9q6e/1/
You can try to check, if Parent contains the child in MouseOut():
var ref=event.toElement || event.relatedTarget; // This line is pseudocode
if(!Parent.contains(ref)){
Parent.removeChild(child);
}