If I have some ".blocks"
body
section
.main-container
.container
.block
.blcok
And I need add className for all parents block, div, section, body.
When I use jquwry I wrote like this:
$(".block").parents().addClass('block-wrapp');
But now I must use native js.
window.onload = function() {
let block = document.getElementsByClassName('block');
for( let i = 0; i < block.length; i++ ) {
block[i].parentNode.classList.add('block-wrapp');
}
};
I try use
let mainBlock = block[i].parentNode.className;
console.log(mainBlock.parentNode);
But it doesn't work
Try changing your parentNode to parentElement inside the loop
Loop through all parents with a while statement:
// loop through selected elements
for( let i = 0; i < block.length; i++ ) {
// get current looped element
let node = block[i];
// do this as long as we have a parent to go to
while (let parent = node.parentNode) {
// add class on parent
parent.classList.add('block-wrapp');
// move up the tree
node = parent;
}
}
You can improve a bit the performance so you don't visit the same parents and traverse the entire tree over and over again for sibling elements. For this, get a list of processed parents and skip them the next time you encounter them:
let visitedParents = [];
while (parent = node.parentNode && visitedParents.indexOf(parent) === -1) {
// ...
// mark this parent as visited
visitedParents.push(parent);
}
Finally, you should also stop when reaching the body element, otherwise you'll reach html and the document, which I presume you don't want to:
while (node.parentNode !== document.body && .... ) { ... }
Related
As in the title,
how can i define nth parentElement in js in other way than:
var theComment = e.target.parentElement.parentElement.parentElement.parentElement;
You can write a loop for it:
const n = 4
let elem = e.target
for(let i = 0; i < n; i++)
elem = elem.parentElement
console.log(elem)
However, as mentioned in the comments, if you're looking for a specific element, it might be better to just write a selector for the element you're looking for.
This applies to your case even more, as event.target can return child nodes of the watched element (if the event bubbles), making your parent count shift around a bit.
You might define a function which ascends up in the parent hierarchy with a loop, e.g.:
function nthParent(n, el) {
while (n--) {
el = el.parentElement;
if (!el) return null;
}
return el;
}
I'm very new at recursion, and have been tasked with writing getElementsByClassName in JavaScript without libraries or the DOM API.
There are two matching classes, one of which is in the body tag itself, the other is in a p tag.
The code I wrote isn't working, and there must be a better way to do this. Your insight would be greatly appreciated.
var elemByClass = function(className) {
var result = [];
var nodes = document.body; //<body> is a node w/className, it needs to check itself.
var childNodes = document.body.childNodes; //then there's a <p> w/className
var goFetchClass = function(nodes) {
for (var i = 0; i <= nodes; i++) { // check the parent
if (nodes.classList == className) {
result.push(i);
console.log(result);
}
for (var j = 0; j <= childNodes; j++) { // check the children
if (childNodes.classList == className) {
result.push(j);
console.log(result);
}
goFetchClass(nodes); // recursion for childNodes
}
goFetchClass(nodes); // recursion for nodes (body)
}
return result;
};
};
There are some errors, mostly logical, in your code, here's what it should have looked like
var elemByClass = function(className) {
var result = [];
var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
(function goFetchClass(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ( pattern.test(nodes[i].className) ) {
result.push(nodes[i]);
}
goFetchClass(nodes[i].children);
}
})([document.body]);
return result;
};
Note the use of a regex instead of classList, as it makes no sense to use classList which is IE10+ to polyfill getElementsByClassName
Firstly, you'd start with the body, and check it's className property.
Then you'd get the children, not the childNodes as the latter includes text-nodes and comments, which can't have classes.
To recursively call the function, you'd pass the children in, and do the same with them, check for a class, get the children of the children, and call the function again, until there are no more children.
Here are some reasons:
goFetchClass needs an initial call after you've defined it - for example, you need a return goFetchClass(nodes) statement at the end of elemByClass function
the line for (var i = 0; i <= nodes; i++) { will not enter the for loop - did you mean i <= nodes.length ?
nodes.classList will return an array of classNames, so a direct equality such as nodes.classList == className will not work. A contains method is better.
Lastly, you may want to reconsider having 2 for loops for the parent and children. Why not have 1 for loop and then call goFetchClass on the children? such as, goFetchClass(nodes[i])?
Hope this helps.
I have written some code that allows one element on a page to be expanded to full screen and contracted back to its original size. This code works by saving the states of other elements on the page, altering their properties, and restoring them. This change has to survive a postback, so I'm attempting to use JSON and a hidden input element to preserve the state changes.
The element in question is nested within multiple IFRAMEs. Thus I have to save the document model in which the element resides. However, this causes the JSON conversion to choke. I need a way to resolve this problem that can be easily converted to JSON and back.
Here is the pertinent code:
// e.uniqueID : A unique identifer for the object.
// e.doc: The document model to which the element belongs (so we can find it later).
// e.style: The original style of the element.
function my_preserveElement(gn,elem)
{
if (elem == null) return;
if (elem.style == null) return;
if (elem.id == '') elem.id = PseudoGuid.GetNew();
var e = new Object();
e.uniqueID = elem.id;
e.doc = elem.document;
var cssString;
cssString = elem.style.cssText;
if( typeof(cssString) != 'string' ) { cssString = elem.getAttribute('style'); }
e.style = cssString;
me_aObjectStore[gn][me_aObjectStore[gn].length] = e;
}
function my_restoreElements(gn)
{
for (i = 0; i < me_aObjectStore[gn].length; i++)
{
var e = me_aObjectStore[gn][i];
var elem = e.doc.getElementById(e.uniqueID);
elem.style.cssText = e.style;
elem.setAttribute('style',e.style);
}
me_aObjectStore[gn] = null;
}
Discovered that since the code in question is running in the innermost frame, I need only walk up the frame tree, searching each level for each element by ID. The restore function becomes as follows, and there is no need to keep track of each element's location (just its unique ID).
function my_restoreElements(gn)
{
for (i = 0; i < my_aObjectStore[gn].length; i++)
{
var e = my_aObjectStore[gn][i];
// Find the element in this window or one of its parents.
// Because we need to check the top level frame and w.parent == w.self for that frame,
// we use the number of counts to keep the loop from being endless.
var w = window;
var elem = null;
var count = 0;
do {
elem = w.document.getElementById(e.uniqueID);
w = w.parent;
count++;
} while ((elem == null) && (count < 3))
if (elem != null) {
elem.style.cssText = e.style;
elem.setAttribute('style',e.style);
}
} //for
my_aObjectStore[gn] = null;
}
Note that I explicitly walk up only three levels. That's because in my specific case, the frames are that few levels deep. Other applications that can use this solution may need a deeper depth.
I'm trying to remove an element by tag name using javascript. I set up a click handler for a button called "clear". I'm trying to use the function the function clear to remove all of the li elements from a list. This is what I have so far:
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (k = list.length; k >= 0; k++) {
var parent = list.parentNode;
parent.removeChild(list[k]);
}
}
Where "test" is the name of a ul element i have in the HTML. I'm getting a message in the console that parent is undefined. Any suggestions on how I need to modify the code so that I can delete the li elements?
The problem seems to be that you’re starting at list.length and incrementing while k >= 0. Infinite loop.
Apart from that, you need to use list[k].parentNode, and you’re not declaring k, so:
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (var k = list.length - 1; k >= 0; k--) {
var item = list[k];
item.parentNode.removeChild(item);
}
}
Almost there, two small errors :
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (var k = list.length; k-- > 0; ) { // decrement from list.length-1 to 0
var parent = list[k].parentNode; // you need the parent of the item, not the list
parent.removeChild(list[k]);
}
}
Note that decrementing instead of incrementing is the thing to do as the opposite order would make you skip some items of the dynamic nodelists.
You can generalize in somethig like that:
function clear(list) {
for (var i = list.length, li; li = list[--i];)
li.parentNode.removeChild(li);
}
clear(document.getElementById("test").getElementsByTagName("li"));
Your main errors was k++ instead of k-- and list.parentNode instead of list[k].parentNode – list is a NodeList, doesn't have a parent; each element of the list can have a different parent.
The NodeList returned by getElementsByTagName is (usually) live (dynamic). This means that it's length decreases as nodes are removed. You must therefore consider this in your loop.
Here are some more examples of how to get around it.
function clear() {
var nodes = document.getElementById("test").getElementsByTagName("li"),
i = nodes.length; // one var statement at beginning
// Choose one of
while (i--) { // same as `i-->0`
nodes[i].parentNode.removeChild(nodes[i]); // remove nodes from end
}
// OR
while (nodes.length) { // same as `nodes.length>0`, okay as dynamic list
nodes[0].parentNode.removeChild(nodes[0]); // remove nodes from start
}
// OR
for (; i >= 0; --i) {
nodes[i].parentNode.removeChild(nodes[i]); // remove nodes from end
}
// etc
}
HTML :
<ul id="test" type="bullet">
<li>apple</li>
<li>Orange</li>
</ul>
<button onclick="Clearall()">Click me</button>
If your UL tag only contains the LI tag then try like below... It will help you..
function Clearall()
{
document.getElementById("test").innerHTML="";
}
If your UL tag contains LI tag and other Tags then try like below.. It will help you..
Javascript :
function Clearall()
{
var ul = document.getElementById("test");
var lis = ul.getElementsByTagName("li");
while(lis.length > 0)
{
ul.removeChild(lis[lis.length - 1]);
}
}
I have the following:
for (var i = 0; i < children.length; i++){
if(hasClass(children[i], "lbExclude")){
children[i].parentNode.removeChild(children[i]);
}
};
I would like it to loop through all children's children, etc (not just the top level). I found this line, which seems to do that:
for(var m = n.firstChild; m != null; m = m.nextSibling) {
But I'm unclear on how I refer to the current child if I make that switch? I would no longer have i to clarify the index position of the child. Any suggestions?
Thanks!
Update:
I'm now using the following, according to answer suggestions. Is this the correct / most efficient way of doing so?
function removeTest(child) {
if (hasClass(child, "lbExclude")) {
child.parentNode.removeChild(child);
}
}
function allDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = temp.childNodes;
for (var i = 0; i < children.length; i++) {
allDescendants(children[i]);
};
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
doSomethingToNode(child);
}
}
You loop over all the children, and for each element, you call the same function and have it loop over the children of that element.
Normally you'd have a function that could be called recursively on all nodes. It really depends on what you want to do to the children. If you simply want to gather all descendants, then element.getElementsByTagName may be a better option.
var all = node.getElementsByTagName('*');
for (var i = -1, l = all.length; ++i < l;) {
removeTest(all[i]);
}
There's no need for calling the 'allDescendants' method on all children, because the method itself already does that. So remove the last codeblock and I think that is a proper solution (á, not thé =])
function removeTest(child){
if(hasClass(child, "lbExclude")){
child.parentNode.removeChild(child);
}
}
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = allDescendants(temp);
You can use BFS to find all the elements.
function(element) {
// [].slice.call() - HTMLCollection to Array
var children = [].slice.call(element.children), found = 0;
while (children.length > found) {
children = children.concat([].slice.call(children[found].children));
found++;
}
return children;
};
This function returns all the children's children of the element.
The most clear-cut way to do it in modern browsers or with babel is this. Say you have an HTML node $node whose children you want to recurse over.
Array.prototype.forEach.call($node.querySelectorAll("*"), function(node) {
doSomethingWith(node);
});
The querySelectorAll('*') on any DOM node would give you all the child nodes of the element in a NodeList. NodeList is an array-like object, so you can use the Array.prototype.forEach.call to iterate over this list, processing each child one-by-one within the callback.
If you have jquery and you want to get all descendant elements you can use:
var all_children= $(parent_element).find('*');
Just be aware that all_children is an HTML collection and not an array. They behave similarly when you're just looping, but collection doesn't have a lot of the useful Array.prototype methods you might otherwise enjoy.
if items are being created in a loop you should leave a index via id="" data-name or some thing. You can then index them directly which will be faster for most functions such as (!-F). Works pretty well for 1024 bits x 100 items depending on what your doing.
if ( document.getElementById( cid ) ) {
return;
} else {
what you actually want
}
this will be faster in most cases once the items have already been loaded. only scrub the page on reload or secure domain transfers / logins / cors any else and your doing some thing twice.
If you use a js library it's as simple as this:
$('.lbExclude').remove();
Otherwise if you want to acquire all elements under a node you can collect them all natively:
var nodes = node.getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (hasClass(n, 'lbExclude')) {
node.parentNode.removeChild(node);
}
}
To get all descendants as an array, use this:
function getAllDescendants(node) {
var all = [];
getDescendants(node);
function getDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
getDescendants(child);
all.push(child);
}
}
return all;
}
TreeNode node = tv.SelectedNode;
while (node.Parent != null)
{
node = node.Parent;
}
CallRecursive(node);
private void CallRecursive(TreeNode treeNode)
{
foreach (TreeNode tn in treeNode.Nodes)
{
//Write whatever code here this function recursively loops through all nodes
CallRecursive(tn);
}
}