Find child element's index in the HTML markup - javascript

I want to find the actual index (in the HTML markup) where a child of a element is located.
Say I have the following markup:
<p id="foo">Lorem <i>ipsum</i> d<strong>ol</strong>ar</p>
And the following code:
var $foo = $('#foo');
var children = $foo.children();
for (var i = 0; i < children.length; i++) {
console.log(children[i]);
}
This outputs the elements <i>ipsum</i> and <strong>ol</strong>. Is there any way to calculate that these elements start at the HTML index 6 and 20 of the parent element #foo? I would like to avoid using a REGEX approach based on the tagName of the element, but that is the only solution I've come up with this far.
Are there any solutions/APIs that I've overlooked that can solve this without overcomplicating things too much? I'd like to make this as generic as possible, with the support of nested tags too, where e.g. #foo > i is the base element and I look inside it to see if there are any nested tags there.
It is also kind of tricky to Google this as most questions/answers deals with actual position, and not the position in the HTML markup.
Working example:
var $foo = $('#foo');
var startIndex = 0;
var children = $foo.children();
for (var i = 0; i < children.length; i++) {
console.log($foo.html());
console.log('<' + $(children[i]).get(0).tagName.toLowerCase());
var this_index = $foo.html().substr(startIndex).indexOf('<' + $(children[i]).get(0).tagName.toLowerCase());
console.log(startIndex + this_index);
console.log('----');
// Update startIndex (to avoid returning the first occurence if multiple children of same type)
startIndex = this_index;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="foo">Lorem <i>ipsum</i> d<strong>ol</strong>ar</p>
Output:
Lorem <i>ipsum</i> d<strong>ol</strong>ar
<i
6
----
Lorem <i>ipsum</i> d<strong>ol</strong>ar
<strong
20
----
However, this is a rather ugly solution and is based on string matching. Is there any way of doing this search/match based on the child element instead? That would limit the possibility that the markup is invalid (for example capitalized HTML tag).

$foo.html().indexOf(children[i].outerHTML) should work

You could simply use indexOf on the string containing inner html of the parent node and search for string containing outer html of the child node:
var haystack = document.querySelector('#foo');
var needle = haystack.querySelector('i');
var startIndex = haystack.innerHTML.indexOf(needle.outerHTML);
var endIndex = startIndex + needle.outerHTML.length;
document.writeln('start: ' + startIndex + '<br> end: ' + endIndex);
<p id="foo">Lorem <i>ipsum</i> d<strong>ol</strong>ar</p>
Similarly it also works if you replace i with strong and yields start: 20 end:39

Related

javascript parse text from <a href> links

Lets say I have
ThisTextChanges
ThisTextChanges
ThisTextChanges
ThisTextChanges
I want to iterate through these and get the "ThisTextChanges" which are some numbers that changes, most accurately timers.
How can i achieve that? jquery is fine.
They are inside a div with id "main_container".
I need to put the text in a var so the href is importanto to know which var i use for each one.
Lets break the task down into several steps:
Get a handle to all of our links (document.querySelectorAll)
learn how to get the current text of an a tag (childNode[0].nodeValue)
put it all together (Array.from, Array.map)
Get a handle to all of our links:
we will use document.querySelectorAll to get list of all nodes that match our selector. here I'm just going to use the selector a, but you probably have a class that specifies these links vs other links on the page:
var links = document.querySelectorAll('a');
Get the text of a link
This one is a bit more complicated. There are several ways to do this, but one of the more efficient ways is to loop through the child nodes (which will mostly be text nodes), and append the node.nodeValue for each one. We could probably get away with just using the nodeValue of the first child, but instead we'll build a function to loop through and append each.
function getText(link){
var text = "";
for (var i = 0; i < link.childNodes.length; i++){
var n = link.childNodes[i];
if (n && n.nodeValue){
text += n.nodeValue;
}
}
return text;
}
Put it all together
To put it all together we will use Array.map to turn each link in our list into the text inside it. This will leave us with an array of strings. However in order to be able to pass it to Array.map we will have to have an array, and document.querySelectorAll returns a NodeList instead. So to convert it over we will use Array.from to turn our NodeList into an array.
function getText(link){
var text = "";
for (var i = 0; i < link.childNodes.length; i++){
var n = link.childNodes[i];
if (n && n.nodeValue){
text += n.nodeValue;
}
}
return text;
}
var linkTexts = Array.from(document.querySelectorAll('a'))
.map(getText);
console.log(linkTexts);
this is text
this is some more text
You can just add condition in the a selector as follows:
var array = [];
$('#main_container a[href="/example2"]').each(function(){
array.push($(this).html());
});
console.log(array);
You can iterate and store them in an Array
var arr = [];
$("a").each(function(){
arr.push($(this).text());
console.log( arr );
});
you can achieve that in may ways. this example using for loop.
var main_container = document.getElementById("main_container");
var items = main_container.getElementsByTagName("a");
for (var i = 0; i < items.length; ++i) {
// do something.....
}
var array = [];
$('#main_container a').each(function(){
array.push($(this).html());
});
console.log(array);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main_container">
ThisTextChanges 1
ThisTextChanges 2
ThisTextChanges 3
ThisTextChanges 4
</div>
Please try:
$('#main_container > a[href]').each(function() {
var tes = $(this).attr('href').substring(1);
window[tes] = $(this).text();
});
123 will produce var named example1 with value 123, and so on.

Getting a string within a string Javascript

I have an html page with a few divs on it. Each div has one of these classes: tabCurrent, tabVisit, tabNext. I'm trying to search the string.className and get a substring that starts with "tab" but returns the full word, tabCurrent, etc.
I've tried various functions such as string.match(/tab\w/), string.exec(), string.includes(). A lot of the functions that find the string within a string, only return a Boolean..Or, you need to know the index and length already. Is there a good way to do this without using a while loop and starting at a known index and continuing until a white space to build the string?
EDIT: I've reworded my question
Say I have a string
var className = 'someClass tabCurrent tabVisit someOtherClass';
I want to get the classes that start with "tab". I'm trying to achieve this using string.match(). Is there a regex expression to pass in that would achieve this?
If there is only one tabSomething class you may use a match like this:
'someOtherClass tabVisit'.match(/\btab.+?\b/); //['tabVisit']
For multiple matches you have to enable the global g flag on the regex.
This will return "tabCurrent" for <div class="tabCurrent">:
div.className.match(/tab.+/g);
Here you go.
Collect all divs
Loop each div and get its className
Split className to array of classes
Match each class against regex that validates it starts with tab: /^tab/
If it matches insert to array
var divs = document.getElementsByTagName('div');
var classRegex = /^tab/;
var matchedClasses = [];
for (var i = 0; i< divs.length; i++) {
var currentDiv = divs[i];
var className = currentDiv.className;
var classNames = className.split(' ');
for (var curClass of classNames) {
if (curClass.match(classRegex)) {
matchedClasses.push(curClass);
}
}
}
console.log(matchedClasses)
<div class="tabCurrent"></div>
<div class="wrapper tabOther tab-three"></div>
<div class="different"></div>

JavaScript use for loop to create p tag with innerHTML content filled in

i am using a for loop to generate paragraph tags based on the length of my array. I want each of these p tags generated to have the same innerHTML. I can get the tags to generate with the class name but the innerHTML remains blank.
I have tried the following to no avail, not sure what I am doing wrong.
for (i = 0; i < numArray.length; i++) {
var line = document.createElement("p");
line.className = "line";
document.body.appendChild(line);
var b = document.getElementsByClassName("line");
b.innerHTML = "|";
}
You don't need to call getElementsByClassName you can change the innerHTML of line since you already have the reference to the DOM element.
for (i = 0; i < numArray.length; i++) {
var line = document.createElement("p");
line.className = "line";
line.innerHTML = "|";
document.body.appendChild(line);
}
And explaining why it didn't work, it's because getElementsByClassName returns a collecion of elements, you need to loop through them.
getElementsByClassName should return an array of elements, not a single element. You could try: getElementsByClassName('line')[i], if there is some reason you are doing that specifically.
Note: getElementsByClassName('line')[i] may not refer to the object you just created, unless there are no other "line"s on the page. It scans the document for all elements that have a class called line, which could be paragraphs or other element types.
For a better alternative, please refer to changes made below. This:
caches the numArray length into a variable, so you are not performing that operation at each loop iteration
sets the HTML and ClassName of the element you created before attaching it to the document; which has a number of performance benefits
does not unnecessarily do a DOM lookup for elements, which is expensive
uses the var keyword to avoid scoping conflicts for loop variables
JS Fiddle:
for ( var i=0, n=numArray.length; i < n; i++) {
var line = document.createElement("p");
line.className = "line";
line.innerHTML = '|';
document.body.appendChild(line);
}

Replace string of text javascript

Im trying to replace a string of text for another string of text here is my code plus js fiddle
HTML
<div class="label">Rating:</div>
<div class="data rating">****</div>
Javascript
var str=document.getElementsByClassName("data" ,"raiting").innerHTML;
var n=str.replace(/\*/g,"star");
document.getElementsByClassName("data", "raiting").innerHTML=n;
Demo
http://jsfiddle.net/sgGQz/1/
document.getElementsByClassName() method returns, as its name suggests, a collection (HTMLCollection) of elements, not a single one -even if there's just a single element with the given classname(s) in DOM.
You need to go through each of them in order to make such a replacement. For example:
var elements = document.getElementsByClassName("data rating");
for (var i = 0, l = elements.length; i < l; i++) {
elements[i].innerHTML = elements[i].innerHTML.replace(/\*/g, 'star');
}
JSFiddle.
Alternatively, if you know for sure that there should be only a single element, you can assign it directly:
var elementToAdjust = document.getElementsByClassName("data rating")[0];
// ...
If you only have one occurrence of the element this will work:
var str=document.getElementsByClassName("data rating")[0].innerHTML;
var n=str.replace(/\*/g,"star");
document.getElementsByClassName("data rating")[0].innerHTML=n;
If multiple data rating elements exist use:
var elems =document.getElementsByClassName("data rating");
for(var i = 0; i < elems.length; i++){
elems[i].innerHTML = elems[i].innerHTML.replace(/\*/g,"star");
}
Both method correct some flaws in the original code.
First, rating was misspelled in the argument passed to getElementsByClassName. Second, getElementsByClassName() uses class names delimited by spaces to select elements with multiple classes, instead of multiple arguments. Get elementsByClassName returns an array of elements which must be iterated through.
JS Fiddle: http://jsfiddle.net/sgGQz/5/
You need to check again for getElementsByClassName,It returns node-List, so you can do like this and You can loop through then after each element and set your value
var str=document.getElementsByClassName("data" ,"raiting")[0].innerHTML;
var n=str.replace(/\*/g,"star");
document.getElementsByClassName("data", "raiting")[0].innerHTML=n;
Here is the example as you have only one occurance

Javascript regex to replace text div and < >

var text='<div id="main"><div class="replace">< **My Text** ></div><div>Test</div></div>'
I want to replace div with class="replace" and html entities < > comes inside that div with some other text.
I.e the output :
'<div id="main"> Hello **My Text** Hello <div>Test</div> </div>'
I've tried
var div = new RegExp('<[//]{0,1}(div|DIV)[^><]*>', 'g');
text = text.replace(div, "Hello");
but this will replace all div.
Any help gratefully received!
If a Jquery solution is acceptable:
text = $(text) // Convert HTML string to Jquery object
.wrap("<div />") // Wrap in a container element to make...
.parent() // the whole element searchable
.find("div.replace") // Find <div class="replace" />
.each(function() // Iterate over each div.replace
{
$(this)
.replaceWith($(this).html() // Replace div with content
.replace("<", "<sometext>")
.replace(">", "</sometext>")); // Replace text
})
.end().html(); // return html of $(text)
This sets text to:
<div id="main"><sometext> My Text </sometext><div>Test</div></div>
And to replace it back again:
text = text.replace('<sometext>', '<div class="replace"><')
.replace('</sometext>', '></div>');
http://api.jquery.com/jquery/#jQuery2
http://api.jquery.com/each/
http://api.jquery.com/find/
http://api.jquery.com/html/
In pure JS it will be something like this:
var elements = document.getElementsByClassName('replace');
var replaceTag = document.createElement('replacetext');
for (var i = elements.length - 1; i >= 0; i--) {
var e = elements[i];
e.parentNode.replaceChild(replaceTag, e);
};​
Here is one crazy regex which matches what you want:
var text='<div id="main"><div class="replace">< **My Text** ></div><div>Test</div></div>'
var r = /(<(div|DIV)\s+class\s*?=('|")\s*?replace('|")\s*?>)(\s*?<)(.*?)(>\s*?)(<\/(div|DIV)\s*?>)/g;
The whole replacement can be made with:
text.replace(r, function () {
return 'Hello' + arguments[6] + 'Hello';
});
Please let me know if there are issues with the solution :).
Btw: I'm totally against regexes like the one in the answer...If you have made it with that complex regex there's probably better way to handle the problem...
Consider using the DOM instead; you already have the structure you want, so swap out the node itself (borrowing heavily from #maxwell's code, but moving children around as well):
var elements = document.getElementsByClassName('replace');
for(var i = elements.length-1; i>= 0; --i) {
var element = elements[i];
var newElement = document.createElement('replacetext');
var children = element.childNodes;
for(var ch = 0; ch < children.length; ++i) {
var child = children[ch];
element.removeChild(child);
newElement.appendChild(child);
}
element.parentNode.insertBefore(newElement,element);
element.parentNode.removeChild(element);
}
For each element of the given class, then, it will move each of its children over to the new element before using that element's position to insert the new element and finally removing itself.
My only questionmark is whether the modification of items in the array return by getElementByClassName will cause problems; it might need an extra check to see if the element is valid before processing it, or you may prefer to write this as a recursive function and process the tree from deepest node first.
It may seem like more work, but this should be faster (no re-parsing of the html after you've changed it, element moves are just reference value assignments) and much more robust. Attempting to parsing HTML may damage your health.
Rereading the question (always a good plan), you begin with the text in a string. If that is truly the start point (i.e. you're not just pulling that out of an innerHTML value), then to use the above just create a temporary parent element:
var fosterer = document.createElement('div');
fosterer.innerHTML = text; // your variable from the question
And then proceed using fosterer.getElementsByClassName.

Categories

Resources