Remove parent element after removing last child element - javascript

I have a list of elements on a page, for the sake of discussion we can say I have the following:
<div id="group_01">
<div id="entry_1-01">stuff x</div>
<div id="entry_1-02">stuff x</div>
</div>
<div id="group_02">
<div id="entry_2-01">stuff x</div>
<div id="entry_2-02">stuff x</div>
</div>
The delete link calls an Ajax request and deletes the entry, after a succesful Ajax call, the entry div is removed from the page. My question is:
How can I remove the containing group div once all of it's entries have been deleted?
I hope that's a detailed enough question. I feel like this isn't anything new, yet two days of search has resulted in nothing.

Before you delete the child element, get its parent, count the number of children, and then after deleting the child, delete the parent if the child count is zero. Here is a quicky piece of sample code:
function d (x)
{
var e = document.getElementById(x);
var p = e.parentNode;
p.removeChild (e);
if (p.childNodes.length == 0) {
var pp = p.parentNode;
pp.removeChild (p);
}
}
I added onclicks to your divs like this:
<div id="group_01">
<div id="entry_1_01">stuff 11<a onclick="d('entry_1_01');" href="#delete">x</a></div>
<div id="entry_1_02">stuff 12<a onclick="d('entry_1_02');" href="#delete">x</a></div>
</div>
I also changed the link to "#delete". You could tidy this up in various ways.

A function like this should would work:
function removeNodeIfEmpty(node) {
var container = document.getElementById(node);
var nodeCount = 0;
for (i = 0; i < container.childNodes.length, i++) {
if (container.childNodes[i].nodeType == 1) {
nodeCount += 1;
}
}
if (nodeCount < 1) {
container.parentNode.removeChild(node);
}
}
This should account for the whitespace issue.

Assuming you do something like this to remove an entry:
entryDiv.parentNode.removeChild(entryDiv);
then you should be able to use the following code to remove the group div when the last child is removed:
var groupDiv = entryDiv.parentNode;
groupDiv.removeChild(entryDiv);
if (!groupDiv.firstChild) {
groupDiv.parentNode.removeChild(groupDiv);
}
...although you need to watch out for whitespace-only text nodes, if these entries haven't been created directly by script.

Really depends what library you're using
http://docs.jquery.com/Traversing/parent#expr
should be a suitable expression

Related

Appending more than two child elements to an element gives a strange behaviour

I have a very strange behaviour and I don't know what causes it.
I loop through all of the id's which javascript can find within my html page. Every time when the 'id' startsWith a specific label, it goes into my if statement.
Within this if-statement I am creating several elements such as P,H1 and HR. Within my 'html'-page I've got 3 unique id's which are DIV's and should go into the if statement. This worked well(picture left [ 1]), until...
I tried to append more than two child elements to those DIV elements. The first two DIV elements are extended as expected. The last one has not been extended though. At the moment that there are more than two elements added to the parent, the 3th DIV (standardArticle3) does not pass the if statement. (picture right [2])
Hopefully the code and scenario picture can make this story clearer:
home.component.ts
var allElements = document.getElementsByTagName("*");
console.log(allElements);
for (var i = 0, n = allElements.length; i < n; ++i) {
var el = allElements[i];
var foundElementId = el.id;
console.log(foundElementId);
if (foundElementId.startsWith("standardArticle"))
{
var standardArticleElement = this.jsonReaderService.getStandardArticleContent(foundElementId.substring(15, foundElementId.length));
var titleElement = document.createElement("h1");
var titleText = document.createTextNode(standardArticleElement.title);
titleElement.appendChild(titleText);
var contentElement = document.createElement("p");
contentElement.innerHTML = standardArticleElement.content;
var thematicBreak = document.createElement("hr");
thematicBreak.style.borderTopColor = "black";
var thematicBreakTwo = document.createElement("hr");
thematicBreakTwo.style.borderTopColor = "black";
// Child elements are all being appended the right way, but the last DIV will never be extended
el.appendChild(titleElement);
el.appendChild(thematicBreak);
el.appendChild(contentElement);
el.appendChild(thematicBreakTwo);
}
}
home.component.html
// other content
<div id="standardArticle1" class="col-sm-6">
</div>
// other content
<div id="standardArticle2" class="col-sm-6 pull-right">
</div>
// other content
<div id="standardArticle3" class="col-sm-6">
</div>
Scenario's 1 and 2.
Does anybody have an idea why this is happening?
document.getElementsByTagName() returns a live HTMLCollection, and your collection includes every element on the page. This means that when you append new elements, these elements get included in the collection but since you iterate up to the original length of the collection, you'll miss the last elements of the collection (the same number of elements as the number of elements you added).
It seems like a better solution to your problem would be to add a class to all elements you want to find and just get those ones with document.getElementsByClassName():
var allElements = document.getElementsByClassName("standard-article");
... and then you add your class name to your HTML:
// other content
<div id="standardArticle1" class="standard-article col-sm-6">
</div>
// other content
<div id="standardArticle2" class="standard-article col-sm-6 pull-right">
</div>
// other content
<div id="standardArticle3" class="standard-article col-sm-6">
</div>
With this approach you don't need your id.startsWith("standardArticle") if statement either.
I think I know what your problem is. Let's see what happens when you loop over all the elements on the page: for (var i = 0, n = allElements.length; i < n; ++i)
If there are 10 elements on the page, then n is set to 10. But then as you reach your if statement and add elements, n is still equal to 10 but you now have additional elements on the page. It looks like Mikael Lennholm said those more concisely and provided a good solution.

efficient way to remove empty div

I'm using <div contenteditable="true">, when I press enter to newline with empty input, it will generate <div><br></div>.
But I just want to remove it from beginning and at the end.
For example:
<div contenteditable="true">
<div><br></div> i want to remove this
abc
<div><br></div> remain
1234
<div><br></div> i want to remove this
<div><br></div> i want to remove this
</div>
for(var x = item.length - 1 ; x >= 0 ; x--){
if($(item[x]).html() == "<br>"){
$(item[x]).remove();
}else{
break;
}
}
for(var x = 0; x <= item.length-1 ; x++){
if($(item[x]).html() == "<br>"){
$(item[x]).remove();
}else{
break;
}
}
Currently I'm using two looping to remove it, But I'm looking a better way to filter it.
Anyone can guide me?
Thanks.
You can actually achieve it with while loop instead of for loop. You need to store the reference for first and last child of the contenteditable div and manipulate it with while loop. Detailed explanation in comments around the code.
$('.remove').on('click', function() {
var firstChild = $('div[contenteditable] div:first');
var lastChild = $('div[contenteditable] div:last');
//store the reference for first and last child of the contenteditable div
while(firstChild.html()=="<br>")//remove all the element's until firstchild's html!="<br>" i.e. is not empty
{
firstChild.remove();//remove it
firstChild= $('div[contenteditable] div:first'); //again store the reference to new first child after remove
}
//same goes with last child
while(lastChild.html()=="<br>")
{
lastChild.remove();
lastChild= $('div[contenteditable] div:last');
}
})
div[contenteditable] {
background-color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable="true">
</div>
<button type="button" class="remove">Remove unwanted</button>
If you want to remove a div which is empty..(Your question states that)....
This may help.....
$("div:empty").remove();
This code removes div that are empty...If thats what You wanted this could help

Deleting all div children from a parent without a for loop in javascript

I have the following code in my javascript:
for (var a = 0; a < cnt; a++) {
var element = document.getElementById("button" + a).getElementsByTagName("div");
for (index = element.length - 1; index >= 0; index--) {
element[index].parentNode.removeChild(element[index]);
}
$("#button" + a).append("Some large html data");
}
I am deleting all the children from parent id "button0","button1"... and so on, which are divs.
and then appending new data to those parents.
However, this particular piece of code takes a long time to execute when the cnt is more than 200, which it usually is. How will I be able to speed it up? Is there an alternative way to delete all children divs without going through each of it?
<div class="main">
<p>hello p1</p>
<p>hello p2</p>
<span> hello world this is span </span>
</div>
$('.main p').remove(); // any number of depths
$('.main > p').remove(); // immediate children
Try this : You can use children selector to remove them, no need to iterate through children.
for (var a = 0; a < cnt; a++) {
//remove div elements inside button
$("#button"+a+" > div").remove();
$("#button" + a).append("Some large html data");
}
DEMO
IF you can have particular class to button div then you can get rid of for loop.
Lets say class="buttonDiv" is assigned to all button div, for example
<div id="button0" class="buttonDiv">
Now your jQuery script to remove child div will be like
$('div.buttonDiv').each(function(){
$(this).children("div").remove();
$(this).append("Some large html data");
});
DEMO with Class
You can use jQuery to delete them, but I don't know how much faster it will be. Under the covers it has to do pretty much the same work:
for (var a = 0; a < cnt; a++) {
$("#button" + a + " div").remove().end().append("Some large html data");
}
It would be much easier if you just add one class to all the buttons you want to remove the children of. Lets say you add the class button to all of them. Then you could just do this:
$('.button > div').remove(); // Removes all direct children divs of the .button element.
Or
$('.button div').remove(); // Removes all divs inside the `.button` element.

Javascript efficient approach for changing inner dom elements of a list item

I have a unordered list with 12 list items inside it
<ul class="rb-grid" id="list">
<li class="view view-fifth">
<div class="mask">
<h2>Article Name</h2>
<p>Brief</p>
Read More
</div>
</li>
...
...
</ul>
Now what i want is that on page load i have to change the content of these h2 and p tags, now while i can do this by hardcoding every list item, but can anyone tell me a better way to make all changes at once by using javascript or jquery anything....
Now i found something like this in dojo , this will make clear what i want actually -
var items = registry.byId("list").getChildren();
array.forEach(items, function(item, idx) {
item.onClick = function(evt) {
};
});
I want to do some such thing to change the contents of the h2 and the p tags inside every list items
Try this: (jquery)
var lis = $('.rb-grid').children('li');
for(var i = 0; i < lis.length : i++){
$(lis).eq(i).find('p').html("change to something");
$(lis).eq(i).find('h2').html("change to something");
}
js
var x =$('.rb-grid').children('li');
x.find('p').html('change to something');
x.find('h2').html('change to something');
A non jquery way:
var ee = document.getElementById('list').getElementsByTagName('li');
for(i=0; i<ee.length; i++) {
ee[i].getElementsByTagName('h2')[0].textContent = "hello world";
ee[i].getElementsByTagName('p')[0].textContent = "article 2";
}
EDIT: It seems IE previous to IE9 does not have textContent and should use innerText instead. Thanks Mr_Green!
Here for comparison is a more idiomatic jQuery version of Mr_Green's answer:
$('.rb-grid').children('li').each( function( i, element ) {
var $element = $(element);
$element.find('p').html("change to something");
$element.find('h2').html("change to something");
});
OTOH, you may not even need the loop, depending on what you're doing. If you just want to change all the relevant nested p and h2 elements to the same value, then Tushar Gupta's answer is a simpler way to do it.

Count how many elements in a div

I have a div with span inside of it. Is there a way of counting how many elements in a div then give it out as a value. For Example there were 5 span in a div then it would count it and alert five. In Javascript please.
Thank you.
If you want the number of descendants, you can use
var element = document.getElementById("theElementId");
var numberOfChildren = element.getElementsByTagName('*').length
But if you want the number of immediate children, use
element.childElementCount
See browser support here: http://help.dottoro.com/ljsfamht.php
or
element.children.length
See browser support here: https://developer.mozilla.org/en-US/docs/DOM/Element.children#Browser_compatibility
You can use this function, it will avoid counting TextNodes.
You can choose to count the children of the children (i.e. recursive)
function getCount(parent, getChildrensChildren){
var relevantChildren = 0;
var children = parent.childNodes.length;
for(var i=0; i < children; i++){
if(parent.childNodes[i].nodeType != 3){
if(getChildrensChildren)
relevantChildren += getCount(parent.childNodes[i],true);
relevantChildren++;
}
}
return relevantChildren;
}
Usage:
var element = document.getElementById("someElement");
alert(getCount(element, false)); // Simply one level
alert(getCount(element, true)); // Get all child node count
Try it out here:
JS Fiddle
Without jQuery:
var element = document.getElementById("theElementId");
var numberOfChildren = element.children.length
With jQuery:
var $element = $(cssSelectocr);
var numberOfChildren = $element.children().length;
Both of this return only immediate children.
i might add just stupid and easy one answer
<div>this is div no. 1</div>
<div>this is div no. 2</div>
<div>this is div no. 3</div>
you can get how many divs in your doc with:
const divs = document.querySelectorAll('div');
console.log(divs.length) // 3
With jQuery; checks only for spans inside a div:
JSFiddle
$(function(){
var numberOfSpans = $('#myDiv').children('span').length;
alert(numberOfSpans);
})();​
With jQuery you can do like this:
var count = $('div').children().length;
alert( count );​​​
Here's a Fiddle: http://jsfiddle.net/dryYq/1/
To count all descendant elements including nested elements in plain javascript, there are several options:
The simplest is probably this:
var count = parentElement.getElementsByTagName("*").length;
If you wanted the freedom to add more logic around what you count, you can recurse through the local tree like this:
function countDescendantElements(parent) {
var node = parent.firstChild, cnt = 0;
while (node) {
if (node.nodeType === 1) {
cnt++;
cnt += countDescendantElements(node);
}
node = node.nextSibling;
}
return(cnt);
}
Working Demo: http://jsfiddle.net/jfriend00/kD73F/
If you just wanted to count direct children (not deeper levels) and only wanted to count element nodes (not text or comment nodes) and wanted wide browser support, you could do this:
function countChildElements(parent) {
var children = parent.childNodes, cnt = 0;
for (var i = 0, len = children.length; i < len; i++) {
if (children[i].nodeType === 1) {
++cnt;
}
}
return(cnt);
}
The easiest way is to select all the span inside the div which will return a nodelist with all the span inside of it...
Then you can alert the length like the example below.
alert(document.querySelectorAll("div span").length)
<div>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>

Categories

Resources