how does childNode indexing work with javascript? - javascript

For example, if I have
<div>
<h2>Name</h2>
<h3>age</h3>
<span class='fa fa-trash'></span>
</div>
and when clicking the span (icon) I say
this.parentNode.childNodes[1].innerText
it would target the Name but if I say
this.parentNode.childNodes[2].innerText
it would not target Age. why is this? Is there a resource that explains this well? I know the indexing doesn't start at 0 but I don't understand how the indexing work.

DOM is not just the elements you see in your HTML code. In-between each element is a "text node" where text can be displayed. An example of this would be having text in a div below a a header. So...
<div>
<!-- Index 0: Text node -->
<h1>Header Text</h1> <!-- Index 1: Element -->
Description Text <!-- Index 2: Text node -->
</div>
As you can see, you are allowed to insert text in the div without wrapping it in an element. These are called text nodes which you see when you put the text in normal text elements (such as span or p) or buttons (<button>TEXT</button>). So to get around this in you JavaScript code, you could either do it the lazy way;
document.getElementById("ELEMENT_ID").childNodes[INDEX*2 + 1]
or by using the children property;
document.getElementById("ELEMENT_ID").children[INDEX]
The problem with this method is that it only returns 'element' children within the div, so the description text in the above HTML example will not be accessible. ([H1] instead of [Text, H1, Text]), but I suppose that is what you're looking for anyway. :)

Related

Javascript - get element id by inner text

Im looking a way to get an element id by a partial inner html
For exemple, I have this template
<div id="unpredictable-id1">
<label>
<span id="unpredictable-id1"> (unpredictable text) </span> <!-- this span have has the same id than div but has unpredictable content -->
<span>persistant text</span> <!-- this span have no id, no class, no identifier -->
</label>
</div>
I cant guess the <div> id (no suffix, no prefix, no other attributes ...)
the only way I have to get the element is by a text in an inner span (a text that I can find before)
I've tried thing like identifiers = document.querySelectorAll("[textContent*='persistant text']").id; but always return 'undefined'
Does anyone have a lead?
If you can get a reference to a descendant element, then you can use the .closest() method:
// Get all the span elements and loop through them
document.querySelectorAll("span").forEach(function(element){
// Check the textContent of the element for a match
if(element.textContent === "persistant text"){
// Find the nearest ancestor div
let closest = element.closest("div")
// ...then do whatever you want with it
console.log("The " + closest.nodeName + " has an id of: " + closest.id);
}
});
<div id="unpredictable-id1">
<label>
<span id="unpredictable-id1"> (unpredictable text) </span> <!-- this span have has the same id than div but has unpredictable content -->
<span>persistant text</span> <!-- this span have no id, no class, no identifier -->
</label>
</div>
FYI: ID's must be unique within a document. Having two elements with the same ID is invalid HTML and defeats the purpose of having IDs in the first place.
Also, [textContent*='persistant text'] didn't work because when you pass something inside of [] into querySelector() or querySelectorAll() you are indicating that you are searching for an attribute of an HTML element. textContent is not an attribute of an HTML element, it's a property of a DOM Element Node. So, nothing matched your search and therefore, you got undefined back.

Appending to specific element append text only

I have prepended one div inside some div with many others:
<div class="content">
<div>sss</div>
<div>aaa</div>
<div>bbb</div>
</div>
I added <div>ddd</div> before sss with:
$('.content').prepend('<div></div>');
And when I want to append some new element to new prepended div it add's it as text:
$('.content>div')[0].append('<p>ddd</p>');
If I remove [0] it works but it appends to all divs, I need that [0] to find first div.
To get the first element (that is still a jquery object) you can use .first():
$('.content>div').first().append($('<p>ddd</p>'));
Note that I also wrapped the <p> with $(...) to make it a valid html element (and not text).

Replacing HTML content without using jquery

I have a HTML content like this:
some of the string content <font color=blue>Test content <BR><BR><BR>
<DIV id='idOfTheDiv'>
some more goes here
<P>Test Content</P>
</DIV>
</font>
I want to remove the div without removing it's content, so the resultant data should look like
some of the string content `<font color=blue>Test content <BR><BR><BR>`
some more goes here
<P>Test Content</P>
</font>
Please note that i do not want to remove the content of the div, also i do not want to add any unwanted HTML element just to remove the div. I have tried various techniques but none of them is working at the moment.
I tried this replacing the innerHTML but it did'nt worked. I can not use replaceChild, as
<DIV id='idOfTheDiv'>
some more goes here
<P>Test Content</P>
</DIV>
is a combonation of text plus HTML so CreateTextNode does'nt workks here as it changes all HTML to plain text.
Please suggest. Thanks a Ton..
Loop over the elements inside the div (use childNodes as it also includes text nodes, while children does not).
Place the elements one-by-one before the div using insertBefore.
Remove the div using removeChild.
This will do the trick:
var el = document.getElementById('idOfTheDiv');
while (el.childNodes.length) {
el.parentNode.insertBefore(el.childNodes[0], el);
}
el.parentNode.removeChild(el);
Demo: http://jsfiddle.net/VG5ZF/
el.parentNode.insertBefore(el.childNodes[0], el); moves the first child node outside from element, reducing the length of childNodes NodeList. So in every iteration el.childNodes[0] is going to be next one. Until there are childs.

JavaScript wrapping unwrapped plain text

I have some non-static content on my webpage and I need all unwrapped plain text to be wrapped inside an anchor element with a class, however, I need to maintain the placement of that text.
I've search around on this site and found questions like these which are asking the same question except the text is always in the beginning or the end and the answers always prepend/append the content back into the <div>.
To make the question even more complicated, there are scenarios where the content will be only unwrapped plain text and I'll need to wrap that as well.
My HTML:
<div>
<a>1</a>
<a>2</a>
3
<a>4</a>
</div>
Sometimes:
<div>
1
</div>
I've tried all the answers on this page but they all reorder the text.
Any ideas?
Iterate over all text nodes of the element:
$('div').contents().filter(function() {
return this.nodeType === 3;
}).wrap('<a class="awesomeClass"></a>');
DEMO
.contents() retrieves all child nodes of the element, not only element nodes.
The .filter callback discards all nodes that are not text nodes. It works as follows:
Each DOM node has the property nodeType which specifies its type (what surprise). This value is a constant. Element nodes have the value 1 and text nodes have the value 3. .filter will remove all elements from the set for which the callback returns false.
Then each of the text nodes is wrapped in a new element.
I'm having a whitespace problem.
If your HTML looks like
<div>
1
</div>
then the element has one child node, a text node. The value of the text node does not only consist of the visible character(s), but also of all the whitespace characters, e.g. the line break following the opening tag. Here they are made visible (⋅ is a space, ¬ is a line break):
<div>¬
⋅⋅1¬
</div>
The value of the text node is therefore ¬⋅⋅1¬.
A way to solve this would be to trim the node values, removing trailing and preceding whitespace character:
$('div').contents().filter(function() {
return this.nodeType === 3;
}).each(function() {
this.nodeValue = $.trim(this.nodeValue);
}).wrap('<a class="awesomeClass"></a>');
DEMO

Dealing with selection() range() and DOM to get indices of text in div

I am attempting to put together a java script that, upon a user's selection of some text inside a specific div id, will return that selection's start and end indices ignoring internal html tags.
Consider the following example:
<p>Some text that will be ignored</p>
<div id="X">
<p>Here is some text that should be considered</p>
<p>Here is a bit more <span>tex</span>t in a separate paragraph but the same div id</p>
</div>
Now, If I highlight be considered. Here is and use
var currentRange = window.getSelection().getRangeAt(0);
console.log('startContainer\t'+currentRange.startContainer);
console.log('startOffset\t'+currentRange.startOffset);
console.log('endContainer\t'+currentRange.endContainer);
console.log('endOffset\t'+currentRange.endOffset);
console.log('textLength\t'+currentRange.toString().length);
console.log('text\t'+currentRange.toString())
I get startOffset:29 and endOffset: 4.
My Question:
Is it possible to keep track of which <p> tag I am inside of while using the Range() object? I will eventually have more html elements in this div tag, but want to be able to get the indices range of the selection as though it were all one string, ignoring all html elements inside.
Thanks for all the help!

Categories