Targeting DOM Nodes Isn't Quite Working - javascript

The following HTML markup and script is faulty, but I'm not getting why:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>More Ways To Target Elements</title>
</head>
<body>
<div id="div1">
<div id div="div2">
<p id="p1">Chicago</p>
<p>Kansas City</p>
<p>St. Louis</p>
</div>
</div>
<script>
var div = document.getElementById('div2');
for (var i = 0; i < div.childNodes.length; i++) {
if (div.childNodes[i].childNodes[0].nodeType === 3) {
alert(div.childNodes[i].childNodes[0].innerHTML);
}
}
</script>
</body>
</html>
I tried minifying the whole thing but I get the same results from the console, which says:
Uncaught TypeError: Cannot read property 'childNodes' of null
Any suggestions, anyone?

There are a few problems there:
Your HTML is invalid and you have no element with the id div2 (<div id div="div2"> should be <div id="div2">). That's what's causing the error you're starting out with.
But once you fix it, you'll run into more errors:
Not all nodes have childNodes, only Element nodes do. Specifically, Text nodes do not. But you're assuming all of the child nodes of #div2 are Element nodes. They aren't, some are Text nodes.
If you only want to look at Element nodes in #div2, use div.children rather than div.childNodes. children only contains Elements, not other kinds of nodes.
You're assuming all of the children of #div2 have at least one child node, because (if you implement #2 above) you're doing div.children[i].childNodes[0].nodeType. That will throw an exception if there are no child nodes, because childNodes[0] will give you undefined and you can't read .nodeType from undefined. You need a guard there.
Text nodes don't have innerHTML. If you want the text of a Text node, you use nodeValue.
With those changes:
var div = document.getElementById('div2');
for (var i = 0; i < div.children.length; i++) {
if (div.children[i].childNodes[0]) {
alert(div.children[i].childNodes[0].nodeValue);
}
}
<div id="div1">
<div id="div2">
<p id="p1">Chicago</p>
<p>Kansas City</p>
<p>St. Louis</p>
</div>
</div>
But, if the goal is to show the text content of the children of #div2, I'd probably use querySelectorAll and use textContent (not supported on older IE) or innerText on the elements (which are not the same thing, but the differences don't matter here) instead, as it would be simpler:
// Get the name to use (textContent isn't supported on older IE)
var propName = "textContent" in document.body ? "textContent" : "innerText";
// Get the immediate children of #div2,
// alert their contents
var list = document.querySelectorAll('#div2 > *');
for (var i = 0; i < list.length; i++) {
alert(list[i][propName]);
}
<div id="div1">
<div id="div2">
<p id="p1">Chicago</p>
<p>Kansas City</p>
<p>St. Louis</p>
</div>
</div>

Related

Javascript: Delete a div that contains (3) in a row <BR>'s in it

<div>
<br>
<br>
<br>
</div>
I have an HTML page that has the above div element in it. How can I scan the page an look for a DIV that contains specific items? In this example I know I have three <br> in a row and that's it. The DIV element does not have a class or an ID, and I would like to delete it.
You can target elements in a row by using the sibling selector, +. You can make sure the target elements are a direct child of the div by using the > selector.
You can find the div that contains the three br elements like so:
document.querySelector('div > br + br + br').parentNode;
To remove this element from the DOM use the remove method.
const elToDelete = document.querySelector('div > br + br + br')?.parentNode;
elToDelete?.remove();
Edit:
Added optional chaining syntax to the answer to show how to prevent undefined errors from being thrown.
You can use the :has pseudo-class to select the div directly
var elem = document.querySelectorAll("div:has(>br+br+br)");
console.log([...elem])
elem.forEach(x=>x.remove())
<div id=a> a
<br/>
<br/>
<br/>
</div>
<div id=b> b
<br/>
<br/>
</div>
<div id=c> c
<br/>
<br/>
<not-br/>
<br/>
</div>
You can try this:
[].slice.call(document.getElementsByTagName("div")).forEach(function(div){
var count = 0;
if(div.children.length === 3)
[].slice.call(div.children).forEach(function(br, i){
if(br.nodeName === "BR")
count++;
});
if(count > 2)
div.parentElement.removeChild(div);
});
You can just hide it with CSS
div:has(>br+br+br) {
display: none;
}
<div>1</div>
<div><br/><br/><br/></div>
<div>3</div>
Best solution that will work cross-browser is to tackle this programmatically. Note that :has() pseudo class doesn't work on Firefox, IE or Opera (see https://caniuse.com/css-has).
Here's a concise functional Javascript approach:
[].filter.call(document.querySelectorAll('div'), el => el.querySelectorAll('br').length === 3)

javascript flipping divs with the same class name horizontaly

I have a bunch of divs like this:
What I would like to do is flip the divs, so the first element becomes the last and the last element becomes first, essentially flipping divs around so 1element,2element,3element would become 3element, 2element, 1element.
I am not sure if this is even possible as there is nothing to distinguish those divs as they all have the same id and class name.
This question is similar to this one: Javascript only - sort a bunch of DIVs
except that here divs have the same id, class name so it makes it all harder.
You can first get all elements with your class, convert it into an array and call reverse() function that flips the array, then loop through the reversed array and append children back to your target div.
Sample codes
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div>
Original:
<div class="test">1</div>
<div class="test">2</div>
<div class="test">3</div>
<div class="test">4</div>
<div class="test">5</div>
</div>
<br />
<div id="target">
Reversed:
<!-- Will be created with Javascript -->
</div>
<script>
const reversedDivs = Array.from(document.getElementsByClassName("test")).reverse();
const targetDiv = document.getElementById("target");
// Create another list in reversed order
reversedDivs.forEach(div => {
const newDiv = div.cloneNode(true);
targetDiv.appendChild(newDiv);
})
</script>
</body>
</html>
Here's a working example: https://codesandbox.io/s/html-reverse-list-u3kld?fontsize=14&hidenavigation=1&theme=dark
...as there is nothing to distinguish those divs as they all have the same id and class name...
Ah, but there is! Where they are in their parent. You can get an array of the elements, reverse it, and then append them to the parent, which will move them so that they're in that reverse order:
var container = document.getElementById("container");
var divs = Array.from(container.children);
divs.reverse();
divs.forEach(function(div) {
container.appendChild(div);
});
Live Example:
setTimeout(function() {
var container = document.getElementById("container");
var divs = Array.from(container.children);
divs.reverse();
divs.forEach(function(div) {
container.appendChild(div);
});
}, 800);
.ib {
display: inline-block;
}
<div id="container"><div class="ib">1</div><div class="ib">2</div><div class="ib">3</div><div class="ib">4</div><div class="ib">5</div></div>
Note that I was careful to do the markup so that there wouldn't be any Text nodes in there, because the children collection only includes Elements, not Text nodes. But if you want to reverse all the children, including text nodes, use childNodes instead of children:
setTimeout(function() {
var container = document.getElementById("container");
var divs = Array.from(container.childNodes);
divs.reverse();
divs.forEach(function(div) {
container.appendChild(div);
});
}, 800);
.ib {
display: inline-block;
}
<div id="container">
<div class="ib">1</div>
<div class="ib">2</div>
<div class="ib">3</div>
<div class="ib">4</div>
<div class="ib">5</div>
</div>
Note that I've kept the code to ES5 level (not ES2015+), except that Array.from is more recent. You can easily polyfill it, or use
var divs = Array.prototype.slice.call(container.children/*or childNodes*/);
instead.

Selecting first visible element with classname [duplicate]

This question already has answers here:
Getting first visible element with jQuery
(3 answers)
Closed 3 years ago.
I'm trying to select the first element with a specific class that is also visible. I only want to select the one element and store it as a variable.
var elements = document.getElementsByClassName('className'); // Get all visible elements with classname.
var element= elements[0]; // Return only the first element from this NodeList
For my specific use case, I'm attempting to then retrieve its background image, so this is what I've got so far. My variable returns "undefined".
var bg = element.css('background-image'); // Returns url('http://www.google.com/imageurl')
I'm using jQuery, but plain JavaScript is welcome, too. Whatever gets the job done the best.
Here's what I would do using JQuery:
var element = $(".classname:visible:first");
var bg = element.css('background-image');
element contains the first element of the specified classname that is visible.
You could use the :visible selector. Something like this $('.classname:visible') .
https://api.jquery.com/visible-selector/
$('selector:visible') will get you visible elements matching the given selector
.eq(n) will return the nth node from those matched
.css('backgroundImage') will give you the background image
.match(/"(.+)"/)[1] will then return just the URL of the background image:
var specialElement = $('.example:visible').eq(1);
specialElement.append(specialElement.css('backgroundImage').match(/"(.+)"/)[1]);
body {
color: white;
}
.example {
visibility: hidden;
}
.special {
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<head>
<title>example</title>
</head>
<body>
<header>
<h1>Hello World</h1>
</header>
<main>
<section>
<article class="example" style="background-image:url('https://images.pexels.com/photos/374815/pexels-photo-374815.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500');">
<h2>Bananas</h2>
<p>Bananas are a great source of potassium.</p>
</article>
<article class="example special" style="background-image:url('https://images.pexels.com/photos/707194/pexels-photo-707194.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500');">
<h2>Apples</h2>
<p>An apple a day will keep the doctor away!</p>
</article>
<article class="example" style="background-image:url('https://images.pexels.com/photos/1667580/pexels-photo-1667580.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500');">
<h2>Oranges</h2>
<p>Oranges make great juice.</p>
</article>
</section>
</main>
</body>

querySelectorAll root elements without using :scope

Let's assume we have this HTML structure:
<div id='test-div'>
<div class='random-class-1'>
<div class='useless-element-1'>
</div>
<div class='useless-element-2'>
</div>
<p>Just a paragraph, and useless</p>
</div>
<div class='random-class-2'>
<div class='useless-element-3'>
</div>
<div class='useless-element-4'>
</div>
<div class='useless-element-5'>
</div>
</div>
</div>
I need to select all children "DIV elements" (not grandchildren) inside the first DIV (in this example with id='test-div'), not from document but from element (div) itself.
So, I don't want to use the "query" below because I already have selected the element DIV [object HTMLDivElement]:
// I don't want to use this query
var children = document.querySelectorAll("div > div");
Just an example to achieve this (https://jsfiddle.net/t4gxt65k/):
// I already have selected DIV element
var el = document.getElementById("test-div")
// OR var el = document.querySelectorAll("#test-div");
var children = el.querySelectorAll(":scope > div");
But because of browser incompatibility I don't want to use ":scope"
The real question is:
How can I get the children (only DIV elements) of [object HTMLDivElement] using pure JavaScript?
As an option, you could set a temporary unique attribute for your scope element, and then use querySelectorAll() for its parent with the attribute selector prepended to what you would place after the :scope selector:
// The `scope` variable stores a reference to the scope element.
var attrName = '_scope' + Date.now();
scope.setAttribute(attrName, '');
var children = scope.parentNode.querySelector('[' + attrName + '] > DIV');
I’m not sure about how fast it is though.
Fwiw, specifically for getting child elements, there is the children DOM property:
var children = scope.children;
To get direct children of an element use a combination of parentNode.children or parentNode.childNodes, and Array.prototype.reduce like this:
var children = Array.prototype.reduce.call(el.children, function(acc, e) {
if(e.tagName == "DIV")
acc.push(e);
return acc;
}, []);
Similar to #Ibrahims answer in selecting children but less complicated since it does not address prototype and reduce. Instead it uses Array.from, Array.filter and Element.matches. Element.matches makes it more versatile since it uses a normal selector string (as you would in querySelectorAll).
Array.from(rootElement.children)
.filter(elm => elm.matches('div'))
const rootElement = document.getElementById('test-div')
Array.from(rootElement.children)
.filter(elm=>elm.matches('div'))
.forEach(elm=>elm.classList.add('matched'))
div {
padding: 1rem;
box-shadow: 0 0 0 1px gray inset;
}
.matched {
box-shadow: 0 0 0 1px red inset;
}
<div id='test-div'>
<div class='random-class-1'>
<div class='useless-element-1'></div>
<div class='useless-element-2'></div>
<p>Just a paragraph, and useless</p>
</div>
<div class='random-class-2'>
<div class='useless-element-3'></div>
<div class='useless-element-4'></div>
<div class='useless-element-5'></div>
</div>
</div>

How to get element inside other specific element using its id and getElementById?

I'm writing a really big script and performance is a big deal for me, especially that it has to work as fast as it can in IE (employer requirements). I have to do some innerHTML and stuff like that but for an element that has some class or id and is nested into some other element with some id or class.
I can do it using querySelector(), but as I saw in some performance tests for IE, querySelector is a few times slower than getElementById or even getElementsByClassName(). That's why I want to use these getElement... functions.
Here's an example:
<div id='firstID' class='someClass'>
<div id='secondID' class='someClass2'></div>
</div>
<div id='thirdID' class='someClass'>
<div id='secondID' class='someClass2'></div>
</div>
And I want to get this secondID element but it has to be one in firstID element. Like I said before, I can use querySelector('#firstID #secondID'); or a jQuery equivalent but it is much slower than getElementById() so I'm asking you how can I translate it into getElementById?
P.S. In my tests getElementById was performed 1 300 000 times per second whereas querySelector was perfomed about 400 000 times. So, you see where I'm getting with that.
Never ever use the same ID twice ( ID stand for identity and two elements can't have the same identity ). You can use the class name instead
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="a">
<div id="b" class="someClass2"></div>
</div>
<div id="c">
<div id="d" class="someClass2"></div>
</div>
<script type="text/javascript">
var parent = document.getElementById("a");
var childs = parent.getElementsByClassName("someClass2");
var firstSomeClass2Element = childs[0];
firstSomeClass2Element.innerHTML = "Example";
</script>
</body>
</html>
More about ID:
The Element.id property represents the element's identifier,
reflecting the id global attribute.
It must be unique in a document, and is often used to retrieve the
element using getElementById. Other common usages of id include using
the element's ID as a selector when styling the document with CSS.
https://developer.mozilla.org/es/docs/Web/API/Element/id
Here is the solution you need but is very very bad
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="a">
<div id="b" class="someClass2">a</div>
</div>
<div id="c">
<div id="b" class="someClass2">s</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("a");
var childs = parent.childNodes;
for (var i = 0; i < childs.length; i++) {
if (childs[i].nodeType == Node.ELEMENT_NODE) {
if(childs[i].id == "b") {
console.log("Done");
}
}
}
</script>
</body>
</html>
Example with attached function
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="a">
<div id="b" class="someClass2">a</div>
</div>
<div id="c">
<div id="b" class="someClass2">s</div>
</div>
<script type="text/javascript">
HTMLElement.prototype.findId = function(_id) {
var childs = this.childNodes;
for (var i = 0; i < childs.length; i++) {
if(childs[i].nodeType == Node.ELEMENT_NODE) {
if(childs[i].id == _id) {
return childs[i];
}
}
}
return null;
}
// Usage Example
var parent = document.getElementById("a");
console.dir(parent.findId("b"));
</script>
</body>
</html>

Categories