Failed to execute removeChild on Node - javascript

Other stack answers such as this and this seem to be specialized cases and I believe my case is more generalized. I am doing this in my js:
var markerDiv = document.createElement("div");
markerDiv.innerHTML = "<div id='MyCoolDiv' style='color: #2b0808'>123</div>";
document.getElementById("playerContainer").appendChild(markerDiv);
// after a brief delay, REMOVE the appended child
setTimeout(function(){
var myCoolDiv = document.getElementById("MyCoolDiv");
document.getElementById("playerContainer").removeChild(myCoolDiv);
}, 1500);
Everything works correctly and as expected (the div is correctly appended and I can see it) until removeChild() is called, at which time I get the error Failed to execute 'removeChild' on 'Node'.
What am I doing wrong?

Your myCoolDiv element isn't a child of the player container. It's a child of the div you created as a wrapper for it (markerDiv in the first part of the code). Which is why it fails, removeChild only removes children, not descendants.
You'd want to remove that wrapper div, or not add it at all.
Here's the "not adding it at all" option:
var markerDiv = document.createElement("div");
markerDiv.innerHTML = "<div id='MyCoolDiv' style='color: #2b0808'>123</div>";
document.getElementById("playerContainer").appendChild(markerDiv.firstChild);
// -------------------------------------------------------------^^^^^^^^^^^
setTimeout(function(){
var myCoolDiv = document.getElementById("MyCoolDiv");
document.getElementById("playerContainer").removeChild(myCoolDiv);
}, 1500);
<div id="playerContainer"></div>
Or without using the wrapper (although it's quite handy for parsing that HTML):
var myCoolDiv = document.createElement("div");
// Don't reall need this: myCoolDiv.id = "MyCoolDiv";
myCoolDiv.style.color = "#2b0808";
myCoolDiv.appendChild(
document.createTextNode("123")
);
document.getElementById("playerContainer").appendChild(myCoolDiv);
setTimeout(function(){
// No need for this, we already have it from the above:
// var myCoolDiv = document.getElementById("MyCoolDiv");
document.getElementById("playerContainer").removeChild(myCoolDiv);
}, 1500);
<div id="playerContainer"></div>

For me, a hint to wrap the troubled element in another HTML tag helped. However I also needed to add a key to that HTML tag. For example:
// Didn't work
<div>
<TroubledComponent/>
</div>
// Worked
<div key='uniqueKey'>
<TroubledComponent/>
</div>

The direct parent of your child is markerDiv, so you should call remove from markerDiv as so:
markerDiv.removeChild(myCoolDiv);
Alternatively, you may want to remove markerNode. Since that node was appended directly to videoContainer, it can be removed with:
document.getElementById("playerContainer").removeChild(markerDiv);
Now, the easiest general way to remove a node, if you are absolutely confident that you did insert it into the DOM, is this:
markerDiv.parentNode.removeChild(markerDiv);
This works for any node (just replace markerDiv with a different node), and finds the parent of the node directly in order to call remove from it. If you are unsure if you added it, double check if the parentNode is non-null before calling removeChild.

I was wraped it with <> </> as a parent when I changed it to normal , div , its worked fine

As others have mentioned, myCoolDiv is a child of markerDiv not playerContainer. If you want to remove myCoolDiv but keep markerDiv for some reason you can do the following
myCoolDiv.parentNode.removeChild(myCoolDiv);
JSFiddle

I'd a similar problem in the vue.js project. Then, I got a hint on changing the fragment wrapper to an HTML element. The most common use case for fragments is probably when you need to return multiple elements. With fragments this is easy and you don't need your typical wrapper div for the elements. Its short syntax is <></>.
Basically, I used the fragment pattern in Vue then I got the above error rendering the component dynamically using transition. It appears that the dynamic component(which entails multiple elements) needed to be wrapped with an HTML element, not a fragment.
// Vuejs
<transition
name="router-anim"
enter-active-class="animated animated-enter"
mode="out-in"
leave-active-class="animated animated-exit"
>
<router-view /> // dynamic rendering based on current route using vue-router
</transition>

Related

Jquery put elements in div

You need to put items in a div - <div style = 'flex-direction: column;'>.
Div needs to be created after p withid = "billing_city_field"
and closes after the p withid = "apartment_field".
Tried to do it with this function:
jQuery (document) .ready (function ($) {
$ ("# billing_city_field"). after ("<div style = 'flex-direction: column;'>");
$ ("# apartment_field"). after ("</ div");
});
But the div immediately closes. What should I do?
The issue is because you cannot append start/end tags separately. The DOM works with elements as a whole, so you need to create the entire div element in a single operation.
Given the description of your goal it looks like you're trying to wrap the existing content in a new div. As such you can use nextUntil() (assuming the target elements are siblings) and then wrapAll(). Try this:
jQuery($ => {
let $contents = $("#billing_city_field").nextUntil('#apartment_field').add('#apartment_field');
$contents.wrapAll('<div class="column" />');
});
Note the use of a class attribute in the above example, instead of applying inline style rules.
Question is not super clear but from what I can tell.
I think you have small misunderstanding what .after does with jQuery. After in this case is "structural" and not "time" related. If you check jQuery docs (https://api.jquery.com/after/) for this you can see basically what you need to do.
Simplest way to do this, if these things needs to created and don't exist already on body for example.
$(function(){
var p = $("<p id='apartment_field'>Paragraph test</p>");
$("body").append("<div id='billing_city_field' style='flex-direction: column;'></div>");
$("#billing_city_field").html(p);
});
I've added Paragraph test so result is visible easier.
And one more thing, not sure if it's error with copy/paste but make sure that # and id don't have space in-between like this.
$("#billing_city_field")
$("#apartment_field")
Edit: Looking at the comments maybe something like this, if they exist already? You should clarify the question more.
$("#billing_city_field").append($("#apartment_field").detach());

jQuery adding element to dynamic element

I have a dynamically added div which I want to append in response to a click event.
The initial div is created and rendered when added however trying to add children divs to the first dynamic div does not render - yet in console log the dynamic div shows the new div has been added.
var newDiv = $('<div id="#newDiv'+pID+'" />').css({
display:"inline-block",
width:"90%",
height:"100px",
position:"relative"
})
var newHTML = "<div>some content</div>"
$(newDiv).html(newHTML)
$('#dynDiv'+ID).append($(newDiv))
console.log($('#dynDiv'+pID)) // displays code created successfully
So newDiv is not rendered nor present when "inspecting" the DOM using debugger.
Why is the second attempt to add dynamic content failing ??
Have you remembered to append it to something? Remember, jQuery can have DOM elements present in memory which are not part of the page:
newDiv.appendTo($(parentElement));
eg. http://jsfiddle.net/dTe73/
A couple of other possible errors:
# is not a valid character to put in an id in $('<div id="#newDiv'+pID+'" />')
$('#dynDiv'+ID) looks like a typo for $('#dynDiv'+pID) (or the other way around)
Not an actual error, but redundant use of $: $(newDiv) is absolutely equivalent to newDiv
I found the source of the problem was that the parent div to which I was adding the dynamic div was not unique - there were multiple elements with same name ! This makes sense that it would fail. Thanks for everyones input.
Replace $(newDiv).html(newHTML) with newDiv.html(newHTML)
and $('#dynDiv'+ID).append($(newDiv)) with $('#dynDiv'+ID).append(newDiv)
and it should work.

AppendChild and InsertBefore

I'm having a little problem with hierarchy elements that are created dynamically.
Been trying to use insertBefore so they change place but no luck, wont get any errors but still I get the element under the other.
I have this function that creates a class called dice-window-wrapper and adds it to the page-content-wrapper.
var outerDiv = createElementWithClass('div', 'dice-window-wrapper'),
innerDiv = createElementWithClass('div', 'dice-menubar-wrapper');
outerDiv.appendChild(innerDiv);
document.getElementById("page-content-wrapper").appendChild(outerDiv);
And I get the print <div class="dice-window-wrapper">...</div>
No problem here.
Then I want to add an unordered list with some <li> tags, using this function:
icon_ul = createElementWithOutClass('ul');
var icon_ul = document.getElementById("page-menu-wrapper").appendChild(icon_ul);
icon_li = createElementWithId('li','icon-dice');
icon_ul.appendChild(icon_li);
document.getElementById("ul");
And the print will be <ul><li id="icon-dice"></li></ul>
The problem as I told is that <div class="dice-window-wrapper">...</div> should be under the string <ul><li id="icon-dice"></li></ul>.
But even if I change the icon_ul function from appendChild to insertBefore, nothing seems to change.
Try this:
document.getElementById("page-menu-wrapper").insertBefore(icon_ul, outerDiv);
insertBefore requires 2 parameters: what to add, and before what you want to add it.
See the documentation here:
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
// ^ returns ^add to this element: ^ this new element, ^ before this existing element.
Okay, so the problem here is, assuming:
document.getElementById("page-menu-wrapper").insertBefore(icon_ul, outerDiv);
There, the parent of outerDiv isn't "page-menu-wrapper". Either replace that line with:
document.getElementById("page-content-wrapper").insertBefore(icon_ul, outerDiv);
Or replace:
document.getElementById("page-content-wrapper").appendChild(outerDiv);
// With
document.getElementById("page-menu-wrapper").appendChild(outerDiv);

javascript get child by id

<div onclick="test(this)">
Test
<div id="child">child</div>
</div>
I want to change the style of the child div when the parent div is clicked. How do I reference it? I would like to be able to reference it by ID as the the html in the parent div could change and the child won't be the first child etc.
function test(el){
el.childNode["child"].style.display = "none";
}
Something like that, where I can reference the child node by id and set the style of it.
Thanks.
EDIT: Point taken with IDs needing to be unique. So let me revise my question a little. I would hate to have to create unique IDs for every element that gets added to the page. The parent div is added dynamically. (sort of like a page notes system). And then there is this child div. I would like to be able to do something like this: el.getElementsByName("options").item(0).style.display = "block";
If I replace el with document, it works fine, but it doesn't to every "options" child div on the page. Whereas, I want to be able to click the parent div, and have the child div do something (like go away for example).
If I have to dynamically create a million (exaggerated) div IDs, I will, but I would rather not. Any ideas?
In modern browsers (IE8, Firefox, Chrome, Opera, Safari) you can use querySelector():
function test(el){
el.querySelector("#child").style.display = "none";
}
For older browsers (<=IE7), you would have to use some sort of library, such as Sizzle or a framework, such as jQuery, to work with selectors.
As mentioned, IDs are supposed to be unique within a document, so it's easiest to just use document.getElementById("child").
This works well:
function test(el){
el.childNodes.item("child").style.display = "none";
}
If the argument of item() function is an integer, the function will treat it as an index. If the argument is a string, then the function searches for name or ID of element.
If the child is always going to be a specific tag then you could do it like this
function test(el)
{
var children = el.getElementsByTagName('div');// any tag could be used here..
for(var i = 0; i< children.length;i++)
{
if (children[i].getAttribute('id') == 'child') // any attribute could be used here
{
// do what ever you want with the element..
// children[i] holds the element at the moment..
}
}
}
document.getElementById('child') should return you the correct element - remember that id's need to be unique across a document to make it valid anyway.
edit : see this page - ids MUST be unique.
edit edit : alternate way to solve the problem :
<div onclick="test('child1')">
Test
<div id="child1">child</div>
</div>
then you just need the test() function to look up the element by id that you passed in.
If you want to find specific child DOM element use method querySelectorAll
var $form = document.getElementById("contactFrm");
in $form variable we can search which child element we want :)
For more details about how to use querySelectorAll check this page

How to remove only the parent element and not its child elements in JavaScript?

Let's say:
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
to this:
<div>
pre text
<p>child foo</p>
<p>child bar</p>
nested text
post text
</div>
I've been figuring out using Mootools, jQuery and even (raw) JavaScript, but couldn't get the idea how to do this.
Using jQuery you can do this:
var cnt = $(".remove-just-this").contents();
$(".remove-just-this").replaceWith(cnt);
Quick links to the documentation:
contents( ) : jQuery
replaceWith( content : [String | Element | jQuery] ) : jQuery
The library-independent method is to insert all child nodes of the element to be removed before itself (which implicitly removes them from their old position), before you remove it:
while (nodeToBeRemoved.firstChild)
{
nodeToBeRemoved.parentNode.insertBefore(nodeToBeRemoved.firstChild,
nodeToBeRemoved);
}
nodeToBeRemoved.parentNode.removeChild(nodeToBeRemoved);
This will move all child nodes to the correct place in the right order.
You should make sure to do this with the DOM, not innerHTML (and if using the jQuery solution provided by jk, make sure that it moves the DOM nodes rather than using innerHTML internally), in order to preserve things like event handlers.
My answer is a lot like insin's, but will perform better for large structures (appending each node separately can be taxing on redraws where CSS has to be reapplied for each appendChild; with a DocumentFragment, this only occurs once as it is not made visible until after its children are all appended and it is added to the document).
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
$('.remove-just-this > *').unwrap()
More elegant way is
$('.remove-just-this').contents().unwrap();
Use modern JS!
const node = document.getElementsByClassName('.remove-just-this')[0];
node.replaceWith(...node.childNodes); // or node.children, if you don't want textNodes
oldNode.replaceWith(newNode) is valid ES5
...array is the spread operator, passing each array element as a parameter
Replace div with its contents:
const wrapper = document.querySelector('.remove-just-this');
wrapper.outerHTML = wrapper.innerHTML;
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
Whichever library you are using you have to clone the inner div before removing the outer div from the DOM. Then you have to add the cloned inner div to the place in the DOM where the outer div was. So the steps are:
Save a reference to the outer div's parent in a variable
Copy the inner div to another variable. This can be done in a quick and dirty way by saving the innerHTML of the inner div to a variable or you can copy the inner tree recursively node by node.
Call removeChild on the outer div's parent with the outer div as the argument.
Insert the copied inner content to the outer div's parent in the correct position.
Some libraries will do some or all of this for you but something like the above will be going on under the hood.
And, since you tried in mootools as well, here's the solution in mootools.
var children = $('remove-just-this').getChildren();
children.replaces($('remove-just-this');
Note that's totally untested, but I have worked with mootools before and it should work.
http://mootools.net/docs/Element/Element#Element:getChildren
http://mootools.net/docs/Element/Element#Element:replaces
I was looking for the best answer performance-wise while working on an important DOM.
eyelidlessness's answer was pointing out that using javascript the performances would be best.
I've made the following execution time tests on 5,000 lines and 400,000 characters with a complexe DOM composition inside the section to remove. I'm using an ID instead of a class for convenient reason when using javascript.
Using $.unwrap()
$('#remove-just-this').contents().unwrap();
201.237ms
Using $.replaceWith()
var cnt = $("#remove-just-this").contents();
$("#remove-just-this").replaceWith(cnt);
156.983ms
Using DocumentFragment in javascript
var element = document.getElementById('remove-just-this');
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
147.211ms
Conclusion
Performance-wise, even on a relatively big DOM structure, the difference between using jQuery and javascript is not huge. Surprisingly $.unwrap() is most costly than $.replaceWith().
The tests have been done with jQuery 1.12.4.
if you'd like to do this same thing in pyjamas, here's how it's done. it works great (thank you to eyelidness). i've been able to make a proper rich text editor which properly does styles without messing up, thanks to this.
def remove_node(doc, element):
""" removes a specific node, adding its children in its place
"""
fragment = doc.createDocumentFragment()
while element.firstChild:
fragment.appendChild(element.firstChild)
parent = element.parentNode
parent.insertBefore(fragment, element)
parent.removeChild(element)
If you are dealing with multiple rows, as it was in my use case you are probably better off with something along these lines:
$(".card_row").each(function(){
var cnt = $(this).contents();
$(this).replaceWith(cnt);
});
The solution with replaceWith only works when there is one matching element.
When there are more matching elements use this:
$(".remove-just-this").contents().unwrap();

Categories

Resources