Need help finding/traversing dom in Javascript - javascript

I'm sure this is a redundant question, but I've looked for an hour or so and come up empty-handed so was hoping someone could help...
Looking for a way to use JS (not jquery) to return the class of the li below when searching for 'Chicken' (or whatever the value is).
<li class='113252'>
<span>Chicken</span>
</li>
So was hoping the javascript would return the li class when given the span value (in this case Chicken).
Thanks!

Try this:
var spanArray = document.getElementsByTagName('span');
for (var i=0; i<spanArray.length; i++) {
if(spanArray[i].innerHTML.toUpperCase() === 'CHICKEN')
{
alert(spanArray[i].parentNode.className);
break;
}
}
Now, I'm more familiar with jQuery but seems to work in the fiddle linked here: http://jsfiddle.net/FranWahl/fCzYc/2/ (Updated to include suggested break; after match)
You can add more type checking for the parentNode to ensure it is an li and so on, but this should get you started.
Also, I'm not sure at all how efficient this is in a big document.
Edit
Having read through some comments I have updated my code above to include the break as suggested by ajax333221.
Dennis mentioned that it would be better to call getElementByTagName on the ul.
Given you can have an li without a ul I added it here as separate code as I'm not sure if the OP has ul tags.
Code querying against each ul (jsFiddle here)
var ulArray = document.getElementsByTagName('ul');
var parentFound = false;
for (var i = 0; i < ulArray.length; i++) {
var spanArray = ulArray[i].getElementsByTagName('span');
for (var i = 0; i < spanArray.length; i++) {
if (spanArray[i].innerHTML.toUpperCase() === 'CHICKEN') {
alert(spanArray[i].parentNode.className);
parentFound = true;
break;
}
}
if(parentFound)
{
break;
}
}​

This is by no means fully complete, which is why you should seek a library, but it does two things:
recursively traverse child elements (starting with the document's BODY), to find the supplied text
recursively traverse the parent element to find the supplied parent element tag, once found it will return the class of that parent
JSFiddle: http://jsfiddle.net/vol7ron/bttQN/
function getParentClass(element, parentTag){
if (element.tagName == parentTag.toUpperCase())
return element.className;
return getParentClass(element.parentNode,parentTag);
}
window.findParentClass = function (text,tagName){
var elements = document.getElementsByTagName('body');
for (var n=elements.length; n--;){
var foundClass = (function searchNextChild(el){
if (!el.children.length) {
if (el.textContent == text)
return getParentClass(el,tagName);
return;
}
for (var i=0, n=el.children.length; i<n; i++)
return searchNextChild(el.children[i]);
})(elements[n]);
return foundClass;
}
};
Example Call:
alert( findParentClass('Chicken','li') );

Related

How to write Javascript to search nodes - without getElementsByClassName

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.

removing element by tag name

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]);
}
}

use remove() on multiple elements

this: document.getElementsByClassName('warningmessage').remove(); doesn't work if you have multiple warningmessage elements on the page.
How can I just delete all elements with that class? do I have to use a for each? isn't there a command to deleteall()?
thanks for your tips!
With plain JavaScript you can do this:
var nodes = document.getElementsByClassName('warningmessage');
for(var i = 0; i < nodes.length; i++){
nodes[i].parentNode.removeChild(nodes[i]);
}
So you would first of all get the nodes you are interested in and afterwards iterate over them and remove them from their parents.
Sadly there is no forEach method on NodeList. However, you could this:
var nodes = document.getElementsByClassName('warningmessage');
[].forEach.call(nodes, function (node) {
node.parentNode.removeChild(node);
});
You need to use a loop. The below code shows how you write in "normal" javascript.
var elements = document.getElementsByClassName('warningmessage'),
element;
while (element = elements[0]) {
element.parentNode.removeChild(element);
}
The working demo.
​
This would be super easier using JQuery:
$('.warningmessage').remove();
But it's not that hard when you do it without. getElementsByClassName can return a nodelist of items. So you'll just have to loop through the list and remove each item:
var list = document.getElementsByClassName("warningmessage");
for(var i = list.length - 1; i >= 0; i--)
{
var n = list[i];
n.parentNode.removeChild(n);
}
You can try this
var elms= document.getElementsByClassName('warningmessage');
while(elms.length>0){
for(var i = 0; i < elms.length; i++){
elms[i].parentNode.removeChild(elms[i]);
}
}​
http://jsfiddle.net/gBwjA/
I have this problem before and I end up in this algorithm.
function removeElement(target) {
if(target.hasOwnProperty('length')) {
for(i=0; i<target.length; i++) {
removeElement(target[i]);
}
} else {
target.parentNode.removeChild(target);
}
}
and then you call the function like this:
removeElement(document.getElementById('the-id'));
or if you want to remove an HTML collection of elements then you call the function in this way:
removeElement(document.getElementsByTagName('tag-name'));

How to get all childNodes in JS including all the 'grandchildren'?

I want to scan a div for all childNodes including the ones that are nestled within other elements. Right now I have this:
var t = document.getElementById('DivId').childNodes;
for(i=0; i<t.length; i++) alert(t[i].id);
But it only gets the children of the Div and not the grandchildren. Thanks!
Edit: This question was too vague. Sorry about that. Here's a fiddle:
http://jsfiddle.net/F6L2B/
The body.onload script doesn't run at JSFiddle, but it works, except that the 'Me Second' and 'Me Third' input fields are not being assigned a tabIndex and are therefore being skipped over.
This is the fastest and simplest way, and it works on all browsers:
myDiv.getElementsByTagName("*")
If you're looking for all HTMLElement on modern browsers you can use:
myDiv.querySelectorAll("*")
What about great-grandchildren?
To go arbitrarily deep, you could use a recursive function.
var alldescendants = [];
var t = document.getElementById('DivId').childNodes;
for(let i = 0; i < t.length; i++)
if (t[i].nodeType == 1)
recurseAndAdd(t[i], alldescendants);
function recurseAndAdd(el, descendants) {
descendants.push(el.id);
var children = el.childNodes;
for(let i=0; i < children.length; i++) {
if (children[i].nodeType == 1) {
recurseAndAdd(children[i]);
}
}
}
If you really only want grandchildren, then you could take out the recursion (and probably rename the function)
function recurseAndAdd(el, descendants) {
descendants.push(el.id);
var children = el.childNodes;
for(i=0; i < children.length; i++) {
if (children[i].nodeType == 1) {
descendants.push(children[i].id);
}
}
}
If anyone else wants all nodes within that tree, and not just the elements, here's a 2022-era JS snippet:
function getDescendantNodes(node, all = []) {
all.push(...node.childNodes);
for (const child of node.childNodes)
getDescendantNodes(child, all);
return all;
}
Protip: afterwards, you can filter to your preferred nodeType with the Node global: nodes = getDescendantNodes($0); nodes.filter(n => n.nodeType === Node.TEXT_NODE)

How to Get Element By Class in JavaScript?

I want to replace the contents within a html element so I'm using the following function for that:
function ReplaceContentInContainer(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
ReplaceContentInContainer('box','This is the replacement text');
<div id='box'></div>
The above works great but the problem is I have more than one html element on a page that I want to replace the contents of. So I can't use ids but classes instead. I have been told that javascript does not support any type of inbuilt get element by class function. So how can the above code be revised to make it work with classes instead of ids?
P.S. I don't want to use jQuery for this.
This code should work in all browsers.
function replaceContentInContainer(matchClass, content) {
var elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if((' ' + elems[i].className + ' ').indexOf(' ' + matchClass + ' ')
> -1) {
elems[i].innerHTML = content;
}
}
}
The way it works is by looping through all of the elements in the document, and searching their class list for matchClass. If a match is found, the contents is replaced.
jsFiddle Example, using Vanilla JS (i.e. no framework)
Of course, all modern browsers now support the following simpler way:
var elements = document.getElementsByClassName('someClass');
but be warned it doesn't work with IE8 or before. See http://caniuse.com/getelementsbyclassname
Also, not all browsers will return a pure NodeList like they're supposed to.
You're probably still better off using your favorite cross-browser library.
document.querySelectorAll(".your_class_name_here");
That will work in "modern" browsers that implement that method (IE8+).
function ReplaceContentInContainer(selector, content) {
var nodeList = document.querySelectorAll(selector);
for (var i = 0, length = nodeList.length; i < length; i++) {
nodeList[i].innerHTML = content;
}
}
ReplaceContentInContainer(".theclass", "HELLO WORLD");
If you want to provide support for older browsers, you could load a stand-alone selector engine like Sizzle (4KB mini+gzip) or Peppy (10K mini) and fall back to it if the native querySelector method is not found.
Is it overkill to load a selector engine just so you can get elements with a certain class? Probably. However, the scripts aren't all that big and you will may find the selector engine useful in many other places in your script.
A Simple and an easy way
var cusid_ele = document.getElementsByClassName('custid');
for (var i = 0; i < cusid_ele.length; ++i) {
var item = cusid_ele[i];
item.innerHTML = 'this is value';
}
I'm surprised there are no answers using Regular Expressions. This is pretty much Andrew's answer, using RegExp.test instead of String.indexOf, since it seems to perform better for multiple operations, according to jsPerf tests.
It also seems to be supported on IE6.
function replaceContentInContainer(matchClass, content) {
var re = new RegExp("(?:^|\\s)" + matchClass + "(?!\\S)"),
elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if (re.test(elems[i].className)) {
elems[i].innerHTML = content;
}
}
}
replaceContentInContainer("box", "This is the replacement text.");
If you look for the same class(es) frequently, you can further improve it by storing the (precompiled) regular expressions elsewhere, and passing them directly to the function, instead of a string.
function replaceContentInContainer(reClass, content) {
var elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if (reClass.test(elems[i].className)) {
elems[i].innerHTML = content;
}
}
}
var reBox = /(?:^|\s)box(?!\S)/;
replaceContentInContainer(reBox, "This is the replacement text.");
This should work in pretty much any browser...
function getByClass (className, parent) {
parent || (parent=document);
var descendants=parent.getElementsByTagName('*'), i=-1, e, result=[];
while (e=descendants[++i]) {
((' '+(e['class']||e.className)+' ').indexOf(' '+className+' ') > -1) && result.push(e);
}
return result;
}
You should be able to use it like this:
function replaceInClass (className, content) {
var nodes = getByClass(className), i=-1, node;
while (node=nodes[++i]) node.innerHTML = content;
}
var elems = document.querySelectorAll('.one');
for (var i = 0; i < elems.length; i++) {
elems[i].innerHTML = 'content';
};
I assume this was not a valid option when this was originally asked, but you can now use document.getElementsByClassName('');. For example:
var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);
See the MDN documentation for more.
There are 3 different ways to get elements by class in javascript. But here for your query as you have multiple elements with the same class names you can use 2 methods:
getElementsByClassName Method - It returns all the elements with the specified class present in the document or within the parent element which called it.
function ReplaceContentInContainer(className, content) {
var containers = document.getElementsByClassName(className);
for (let i = 0; i < containers.length; i++) {
containers[i].innerHTML = content;
}
}
ReplaceContentInContainer('box', 'This is the replacement text');
<div class='box'></div>
querySelectorAll Method - It select element on the basic of CSS selectors. Pass your CSS class to it with a dot and it will return all the element having specified class as an array-like object.
function ReplaceContentInContainer(className, content) {
var containers = document.querySelectorAll(`.${className}`);
for (let i = 0; i < containers.length; i++) {
containers[i].innerHTML = content;
}
}
ReplaceContentInContainer('box', 'This is the replacement text');
<div class='box'></div>
I think something like:
function ReplaceContentInContainer(klass,content) {
var elems = document.getElementsByTagName('*');
for (i in elems){
if(elems[i].getAttribute('class') == klass || elems[i].getAttribute('className') == klass){
elems[i].innerHTML = content;
}
}
}
would work
jQuery handles this easy.
let element = $(.myclass);
element.html("Some string");
It changes all the .myclass elements to that text.
When some elements lack ID, I use jQuery like this:
$(document).ready(function()
{
$('.myclass').attr('id', 'myid');
});
This might be a strange solution, but maybe someone find it useful.

Categories

Resources