I need to use pure Javascript for the first time in a long while, and having gotten used to the comfy mattress of jQuery, all the important stuff is escaping me.
I need to select a bunch of divs on regular expression. So I have stuff like this;
<div id="id_123456_7890123"> .. </div>
<div id="id_123456_1120092"> .. </div>
<div id="id_555222_1200192"> .. </div>
<div id="id_123456_9882311"> .. </div>
And I'd need to create a loop that goes through all the divs with an id that begins with id_123456_. How would I go about doing that?
I used jQuery with the :regex filter plugin before, but looking at it, it doesn't seem like there's much I could salvage in a pure javascript rewrite.
In plain javascript, you could do this generic search which should work in every browser:
var divs = document.getElementsByTagName("div"), item;
for (var i = 0, len = divs.length; i < len; i++) {
item = divs[i];
if (item.id && item.id.indexOf("id_123456_") == 0) {
// item.id starts with id_123456_
}
}
Working example: http://jsfiddle.net/jfriend00/pYSCq/
HTML DOM querySelectorAll() method will work here.
document.querySelectorAll('[id^="id_"]');
Borrowed from StackOverFlow here
This works by recursively traversing the whole DOM.
It's possibly not the most efficient, but should work on every browser.
function find_by_id(el, re, s) {
s = s || [];
if (el.tagName === 'DIV' && re.exec(el.id) !== null) {
s.push(el);
}
var c = el.firstChild;
while (c) {
find_by_id(c, re, s);
c = c.nextSibling;
}
return s;
}
var d = find_by_id(document.body, /^id_123456_/);
See http://jsfiddle.net/alnitak/fgSph/
Here you are: http://jsfiddle.net/howderek/L4z9Z/
HTML:
<div id="nums">
<div id="id_123456_7890123">Hey</div>
<div id="id_123456_1120092">Hello</div>
<div id="id_555222_1200192">Sup</div>
<div id="id_123456_9882311">Boom</div>
</div>
<br/>
<br/>
<div id="result"></div>
Javascript:
divs = document.getElementsByTagName("div");
divsWith123456 = new Array();
for (var i = 0;i < divs.length;i++) {
if (divs[i].id.match("id_123456") != null) {
divsWith123456.push(divs[i]);
document.getElementById("result").innerHTML += "Found: divs[" + i + "] id contains id_123456, its content is \"" + divs[i].innerHTML + "\"<br/><br/>";
}
}
Related
I'm trying to remove the excessive closing tags in javascript and anything that follows after that.
Here is a possible sample:
<div class="dummy">
<div class="main">
<div></div>
<img src="a.jpg">
<br>
<img src="b.jpg />
<strong>
<span>text</span>
</strong>
</div>
</div>
***excessive tags below***
</div>
</div>
<div class="footer">
text
</div>
</body>
</html>
Any ideas about how to do it efficiently?
The part I want to extract is always a div, but the problem is that it may have as many nested divs, and I'm not sure how to deal with this scenario.
If it can be done in multiple steps or with callbacks is also fine, as long as it works.
Edit
My question is actually easier than it seems.
The sample always starts with the div that I want to extract.
So all I need is to find the matching closing tag, and filter anything that follows.
Don't care about any other tags...
Don't use regex, from my understanding, you want to retain the dummy class div and the footer class div so why not replace the body with that?
E.g.
var dummy = document.getElementsByClassName('dummy')[0];
var footer = document.getElementsByClassName('footer')[0]
var body = document.getElementsByTagName('body')[0];
body.innerHTML = '';
body.appendChild(dummy);
body.appendChild(footer);
https://jsfiddle.net/1kq11ry2/
data='<div class="dummy"><div class="main"><div></div><img src="a.jpg"><br><div></div><img src="b.jpg /><strong><span>text</span> </strong></div><div><div></div></div><div><div></div></div></div>***excessive tags below***</div></div><div class="footer">text</div></body></html>';
var starting_tags = [];
var closing_tags = [];
var startIndex, index=0;
var searchStrLen = 4;
while ((index = data.indexOf('<div', startIndex)) > -1) {
starting_tags.push(index);
startIndex = index + searchStrLen;
}
index,startIndex=0;
searchStrLen = 6;
while ((index = data.indexOf('</div>', startIndex)) > -1) {
closing_tags.push(index);
startIndex = index + searchStrLen;
}
var nest_level=0;
for (var i=0; i<closing_tags.length && nest_level<closing_tags.length && nest_level<=closing_tags.length; ++i) {
for (var j=0+nest_level; j<starting_tags.length; ++j) {
if (starting_tags[j]<closing_tags[nest_level])
nest_level++;
}
}
result = data.substr(startIndex[starting_tags], closing_tags[nest_level-1]+6);
console.log(nest_level);
console.log(starting_tags);
console.log(closing_tags);
console.log(result);
I was able to solve it. The code above calculates the level of div nesting, and then chops it off if it finds excessive closing tags.
https://jsfiddle.net/89j7yakz/2/
I am working on a website where I dont have any control on the code (functionality side. however even if I had the access I wouldn't be able to make any changes as I am a front end designer not a coder..lol). The only changes I can make is CSS, js.
What I am tring to do:
I got the url on the page like this:
www.test.com/#box1#box3#box5
(I am not sure if I can have more than one ID in a row. please advice. However thats how the developer has done it and I dont mind as there are no issues yet)
the page html
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<div id="box4"></div>
<div id="box5"></div>
I would like to take different ids from the URl and use it to hidhlight the divs with that ID (by addding a class name "highlight")
The result should be like this:
<div id="box1 highlight"></div>
<div id="box2"></div>
<div id="box3 highlight"></div>
<div id="box4"></div>
<div id="box5 highlight"></div>
I would like to learn the smart way of taking just the id numbers from the url and use it to select the div with that paticulat no.
just a quick explanation:
var GetID = (get the id from the URL)
$('#box' + GetID).addClass('highlight');
Try This...
var hashes =location.hash.split('#');
hashes.reverse().pop();
$.each(hashes , function (i, id) {
$('#'+id).addClass('highlight');
});
Working fiddle here
var ids = window.location.hash.split('#').join(',#').slice(1);
jQuery(ids).addClass('highlight');
or in plain JS:
var divs = document.querySelectorAll(ids);
[].forEach.call(divs, function(div) {
div.className = 'highlight';
});
There are various way to resolve this issue in JavaScript. Best is to use "split()" method and get an array of hash(es).
var substr = ['box1','box2']
// Plain JavaScript
for (var i = 0; i < substr.length; ++i) {
document.getElementById(substr[i]).className = "highlight"
}
//For Each
substr.forEach(function(item) {
$('#' + item).addClass('highlight')
});
//Generic loop:
for (var i = 0; i < substr.length; ++i) {
$('#' + substr[i]).addClass('highlight')
}
Fiddle - http://jsfiddle.net/ylokesh/vjk7wvxq/
Updated Fiddle with provided markup and added plain javascript solution as well -
http://jsfiddle.net/ylokesh/vjk7wvxq/1/
var x = location.hash;
var hashes = x.split('#');
for(var i=0; i< hashes.length; i++){
var GetID = hashes[i];
//with jQuery
$('#box' + GetID).addClass('highlight');
//with Javascript
document.getElementById('box' + GetID).className = document.getElementById('box' + GetID).className + ' highlight';
}
I need to create an array which contains all text from a page without jQuery. This is my html:
<html>
<head>
<title>Hello world!</title>
</head>
<body>
<h1>Hello!</h1>
<p>
<div>What are you doing?</div>
<div>Fine, and you?</div>
</p>
Thank you!
</body>
</html>
Here is what i want to get
text[1] = "Hello world!";
text[2] = "Hello!";
text[3] = "What are you doing?";
text[4] = "Fine, and you?";
text[5] = "Thank you!";
Here is what i have tried but seems to not work correctly in my browser:
var elements = document.getElementsByTagName('*');
console.log(elements);
PS. I need to use document.getElementsByTagName('*'); and exclude "script" and "style".
var array = [];
var elements = document.body.getElementsByTagName("*");
for(var i = 0; i < elements.length; i++) {
var current = elements[i];
if(current.children.length === 0 && current.textContent.replace(/ |\n/g,'') !== '') {
// Check the element has no children && that it is not empty
array.push(current.textContent);
}
}
You could do something like this
Demo
result = ["What are you doing?", "Fine, and you?"]
or you could use document.documentElement.getElementsByTagName('*');
Also make sure your code is inside this
document.addEventListener('DOMContentLoaded', function(){
/// Code...
});
If it's just the title you need, you may aswell do this
array.push(document.title);
Saves looping through scripts & styles
If you want the contents of the entire page, you should be able to use
var allText = document.body.textContent;
In Internet Explorer before IE9, there was the property innerText which is similar but not identical. The MDN page about textContent has more detail.
Now one problem here is that textContent will get you the content of any <style> or <script> tags, which may or may not be what you want. If you don't want that, you could use something like this:
function getText(startingPoint) {
var text = "";
function gt(start) {
if (start.nodeType === 3)
text += start.nodeValue;
else if (start.nodeType === 1)
if (start.tagName != "SCRIPT" && start.tagName != "STYLE")
for (var i = 0; i < start.childNodes.length; ++i)
gt(start.childNodes[i]);
}
gt(startingPoint);
return text;
}
Then:
var allText = getText(document.body);
Note: this (or document.body.innerText) will get you all the text, but in a depth-first order. Getting all the text from a page in the order that a human actually sees it once the page is rendered is a much more difficult problem, because it'd require the code to understand the visual effects (and visual semantics!) of the layout as dictated by CSS (etc).
edit — if you want the text "stored into an array", I suppose on a node-by-node basis (?), you'd simply substitute array appends for the string concatenation in the above:
function getTextArray(startingPoint) {
var text = [];
function gt(start) {
if (start.nodeType === 3)
text.push(start.nodeValue);
else if (start.nodeType === 1)
if (start.tagName != "SCRIPT" && start.tagName != "STYLE")
for (var i = 0; i < start.childNodes.length; ++i)
gt(start.childNodes[i]);
}
gt(startingPoint);
return text;
}
Seems to be a one line solution (fiddle):
document.body.innerHTML.replace(/^\s*<[^>]*>\s*|\s*<[^>]*>\s*$|>\s*</g,'').split(/<[^>]*>/g)
This may fail if there are complicated scripts in the body, though, and I know that parsing HTML with regular expressions is not a very clever idea, but for simple cases or for demo purposes it still can be suitable, can't it? :)
Walk the DOM tree, get all the text nodes, get the nodeValue of the text node.
var result = [];
var itr = document.createTreeWalker(
document.getElementsByTagName("html")[0],
NodeFilter.SHOW_TEXT,
null, // no filter
false);
while(itr.nextNode()) {
if(itr.currentNode.nodeValue != "")
result.push(itr.currentNode.nodeValue);
}
alert(result);
Alternate method: Split on the HTML tag's textContent.
var result = document.getElementsByTagName("html")[0].textContent.split("\n");
for(var i=0; i<result.length; i++)
if(result[i] == "")
result.splice(i, 1);
alert(result);
<html>
<head>
<title>Hello world!</title>
</head>
<body>
<h1>Hello!</h1>
<p>
<div>What are you doing?</div>
<div>Fine,
<span> and you? </span>
</div>
</p>
Thank you!
<script type="text/javascript">
function getLeafNodesOfHTMLTree(root) {
if (root.nodeType == 3) {
return [root];
} else {
var all = [];
for (var i = 0; i < root.childNodes.length; i++) {
var ret2 = getLeafNodesOfHTMLTree(root.childNodes[i]);
all = all.concat(ret2);
}
return all;
}
}
var allnodes = getLeafNodesOfHTMLTree(document.getElementsByTagName("html")[0]);
console.log(allnodes);
//in modern browsers that surport array filter and map
allnodes = allnodes.filter(function (node) {
return node && node.nodeValue && node.nodeValue.replace(/\s/g, '').length;
});
allnodes = allnodes.map(function (node) {
return node.nodeValue
})
console.log(allnodes);
</script>
</body>
</html>
<div id = "board>
<div>{abc</div>
<div>def</div>
<div>ghi}</div>
</div>
I want compare every char inside the div at their position and do something when if found { or }
Im aware that this is possible by wrapping every char within <span></span>
Is there a way to do this without using a span? I will use this for brace matching of my code editor project. this is what i've done using span wrapping, but it is so slow..
$exceedingInlineDiv = $('#board_code_dup > div').eq(x);
if( $exceedingInlineDiv.text() == ''){
var chars = '<span> <br> </span>';
$exceedingInlineDiv.html(chars);
}
else{
var chars = jQuery.map($exceedingInlineDiv.text().split(''), function(c) {
return '<span>' + c + '</span>';
});
$exceedingInlineDiv.html(chars.join(''));
}//else
I'm not sure what you want to do, but maybe you want something like this:
var board = document.getElementById("board"),
divs = board.getElementsByTagName("div"),
texts = [], i = 0;
for (; i < divs.length; i++)
texts.push(divs[i].innerHTML);
// texts => ["{abc", "def", "ghi}"]
If I have two elements :
Element A :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
</div>
and Element B :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
<div id="d"></div>
</div>
Is it possible to find out that Element B has more children than Element A, then find where is additional element and create it in Element A?
P.S: In real code new element is loaded with Ajax Request, but I don't want to replace all content with loaded content, I need to add only new content and skip existing one.
P.S.S : In my current code I have Md5 checksum to check if new content is not the same as existing, but if new content have only little changes it replaces all content and this is the problem for me.
A piece of my current code :
window.processResponse = function(data) {
// Note : "data" is Ajax responseText;
if(!data) return false;
var $data = document.createElement("div");
$data.innerHTML = data;
var em = $data.getElementsByTagName("*");
for(var i = 0; i < em.length; i++)
{
var parent = sget(em[i].id); // sget function is : document.getElementById
if(parent)
{
var html = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(html);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
parent.innerHTML = em[i].innerHTML;
}
if(em[i].tagName === "SCRIPT")
{
var code = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(code);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
try{eval(em[i].innerHTML)}catch(ex){log(ex)};
}
}
};
So, you can optimize it but it depends also in which browser are you running this code.
I assumed the follows:
All IDs are unique, and you rely on that. You want to compare basically elements that have the same ID, not the same structure.
As you said, all children have IDs, and you want to compare only children – not nested node
The elements received from the server have only additional children not less. For removing children, you have to add some other code.
Therefore, if you have the same number of children, we assume they're the same (to optimize). If this is not true, then you have to implement the removal of children as well
Said that, I believe that this kind of stuff is more suitable on server side, that should send to the client only the part that are actually modified. That what we do usually – or, if we don't care, replace everything.
var div = document.createElement('div');
div.innerHTML = s;
var root = div.firstChild;
var children = root.children;
var documentRoot = document.getElementById(root.id);
if (documentRoot && documentRoot.children.length < children.length) {
var node = null;
var previousNode = null;
var index = 0;
while ( node = children[index++] ) {
var documentNode = document.getElementById(node.id);
if (!documentNode) {
if (previousNode)
documentRoot.insertBefore(node, previousNode.nextSibling);
else
documentRoot.insertBefore(node, documentRoot.firstChild);
documentNode = node;
}
previousNode = documentNode;
}
previousNode = null;
} else {
// probably append as is somewhere
}
The solution is not so simple. What if the parent, myID, did not exist in sample A but the child nodes were in both samples indicating 3 layers in the DOM that need to be compared and adjusted? How would you compare this:
<div id="papa">
<div id="myID">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
</div>
vs
<div id="papa">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
In this case the comparison becomes more complicated. You will actually need a fully fleshed out XML/HTML language aware diff utility with a merge function. You can play around with Pretty Diff to demonstrate just how complicated this can get, but unfortunately it does not have a merge function so it cannot be a fully automated solution to your problem.