Vanilla Javascript equivalent of jQuery not() - javascript

This is what I'm doing in jQuery:
var text2 = $(node).not("span").text();
console.log(text2);
How do I do this same thing in pure javascript?
I know how to get elements in javascript, but not how to ignore elements and select the rest
var spans = node.getElementsByTagName('span');
I need to get every elements within node that does not contain a <span>
Here is my HTML
<tr>
<td>
<span>Farley</span>
<div class="table-row__expanded-content">
<data-key>Sex: </data-key> <data-value>Male</data-value><br />
<data-key>DOB: </data-key> <data-value>12/08/2010</data-value> <br />
<data-key>Weight: </data-key> <data-value>20 lbs</data-value> <br />
<data-key>Location: </data-key> <data-value>Kennel 2B</data-value> <br />
<data-key>Temperament: </data-key> <data-value>Aggresive</data-value> <br />
<data-key>Allergies: </data-key> <data-value>Sulfa, Penicillin, Peanuts</data-value>
</div>
</td>
<td>Cunningham, Stephanie</td>
<td>Dog</td>
<td>Pomeranian</td>
<td>PQRST1234567</td>
</tr>

Or a quick example that runs in a console on this page:
var startNode = jQuery("li.related-site").get(0); // quick jQ to get a testable node.
var spanLess = [];
var child = startNode.firstChild;
while(child){
if(child.nodeType == 1){
var anySpans = child.getElementsByTagName('span');
if(!anySpans.length) spanLess.push(child);
}
child = child.nextSibling;
}
spanLess;
Based on your comment that you are trying to extract values for use with tablesorter what you might also find useful is a function to extract the text values from a node regardless of markup:
function extractText(node){
if(node.nodeType == 3) return node.nodeValue.trim();
if(node.nodeType == 1){
var buf = [];
var child = node.firstChild;
while(child){
var val = extractText(child);
if(val) buf.push(val);
child = child.nextSibling;
}
return buf.join(' ');
}
return '';
}

Try querySelectorAll
var notSpans = document.getElementsByTagName('div')[0].querySelectorAll(':not(span)');
for (var i = 0; i < notSpans.length; i++) {
notSpans[i].style.color = 'green';
}
<div>
<p>not a span</p>
<span>a span</span>
</div>

Thanks to bknights, I was able to modify his answer to work for my needs.
My full fuction is as follows:
var myTextExtraction = function (node) {
//console.log(node);
var child = node.firstChild;
while (child) {
if (node.getElementsByTagName('span').length) {
childValue = child.nextSibling.innerHTML;
console.log(childValue);
}
else {
childValue = child.nodeValue
console.log(childValue);
}
return childValue;
}
}
Then I create the node object with the tablesorter plugin:
// Load the table sorter
$(".table").tablesorter(
{
textExtraction: myTextExtraction
}
);
This loops through and outputs the text inside <td><span>mytext</span></td> as well as the text in <td>myothertext<td>. I'm using this for the jquery TableSorter plugin to work with complex data inside <td>s

Related

How to change HTML element's text and don't remove children in javascript?

I'm using following code for translation elements with data-i18next attribute:
const elementsToTranslate = document.querySelectorAll('[data-i18next]');
for (let i = 0; i < elementsToTranslate.length; i++) {
elementsToTranslate[i].innerText = i18next.t(elementsToTranslate[i].getAttribute('data-i18next'));
}
But it replaces all child elements.
I have h1 element with span child:
<h1 data-i18next="header-title" class="header__title">
<span data-i18next="header__subtitle" class="header__subtitle"></span>
</h1>
After running translation function it becomes:
<h1 data-i18next="header-title" class="header__title">translated-text</h1>
But I need child items to stay. Without jquery.
Result I need:
<h1 data-i18next="header-title">translated-title
<span data-i18next="header__subtitle">translated-span</span>
</h1>
Create a textnode and prepend it in the element. This should end up in the result you seek according to your edit.
The function i18next.t() is not known, since you did not provide it. Thus I replace it with the attribute text for now.
<html>
<head>
<script>
//Scope for the little translator and the list of textnodes
;(function(ns){
'use strict';
var _List = []; //Stores the created textnodes
//Removes the textnodes from the List
function _removeNodes(){
if(_List && _List.length){
for(var i=_List.length-1; i>=0; i--){
_List[i].parentNode && _List[i].parentNode.removeChild(_List[i])
};
_List = []
}
};
ns.Translator = {
Translate: function(){
_removeNodes();
const elementsToTranslate = document.querySelectorAll('[data-i18next]');
for (let i = 0; i < elementsToTranslate.length; i++){
var tE = elementsToTranslate[i];
var tText = tE.getAttribute('data-i18next'); //i18next.t(tE.getAttribute('data-i18next'));
var tN = document.createTextNode(tText);
_List.push(tN); //The node gets stored, so it can be removed again later
tE.insertBefore(tN, tE.firstChild)
}
}
}
}(window._ = window._ || {}));
window.onload = function(){
_.Translator.Translate()
_.Translator.Translate()
_.Translator.Translate()
}
</script>
</head>
<body>
<h1 data-i18next="header-title" class="header__title">
<span data-i18next="header__subtitle" class="header__subtitle"></span>
</h1>
</body>
</html>
Result:
<h1 data-i18next="header-title" class="header__title">header-title
<span data-i18next="header__subtitle" class="header__subtitle">header__subtitle</span>
</h1>
updated. I want to translate both: title and span inside it.
#caesay subtitle is span and it's better to put it inside

How to get all text from all tags in one array?

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>

find difference in two dom elements then make them same

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.

Remove existing code side of div code

Goal:
If there are any html syntax code or data inside of
<div id="feedEntries"></div>
Then everything should be removed and be contained empty only.
Problem:
What syntax do I need in order to remove every code and data inside of
<div id="feedEntries"></div>
Please remember that i don't want to add any class or id inside of "feedEntries"
<h3>Search</h3>
<div class="content">
<form>
<input type="text" width="15" value="searchword" id="searchTermTextField"><input type="button" name="some_name" value="Sök" id="searchButton">
</form>
<div id="feedEntries">
</div>
</div>
function fetchSearchResults(json) {
var feedEntriesDivElement = document.getElementById('feedEntries');
var ulElement = document.createElement('ul');
if (feedEntriesDivElement.children.length >= 0)
{
// Syntax code to remove the code/data
}
for (var i = 0; i < json.responseData.results.length; i++)
{
var liElement = document.createElement('li');
var personText = document.createTextNode(json.responseData.results[i].titleNoFormatting);
var newlink = document.createElement('a');
newlink.setAttribute('href', json.responseData.results[i].url );
newlink.appendChild(personText);
liElement.appendChild(newlink);
ulElement.appendChild(liElement);
}
feedEntriesDivElement.appendChild(ulElement);
}
Using pure DOM and Javascript (sometimes considered better than altering innerHTML):
if ( feedEntriesDivElement.hasChildNodes() )
{
while ( feedEntriesDivElement.childNodes.length >= 1 )
{
feedEntriesDivElement.removeChild( feedEntriesDivElement.firstChild );
}
}
feedEntriesDivElement.innerHTML = ''; should do the trick.
you can use jquery like this $('#feedEntries').empty()
to remove from javascript please check the post
document.getElementByIf('feedEntries').innerHTML = ''

JS regex to select all <br /> that are within <p></p>'s - for function to indent text after a break

I'd like to select all occurances of <br /> that are within a paragraph <p></p> with a regular expression in JS. Currently I just select all <br /> like this:
var regex = /<br\s*[\/]?>/gi;
But doing this gives me trouble at some point because of what I'm trying to do with the selection. I need a more precise regex since breaks in headlines etc. are irrelevant to me.
If you are wondering about the context of this, I want to replace the <br />'s with two paragraphs with suitable classes to act like a <br /> but to indent the text after the break like this:
function removeEmptyNodes(selector)
{
$(selector).each(function() {
if ($(this).html().replace(/\s| /g, '').length == 0)
$(this).remove();
});
};
function assignIndents()
{
var str = $("#content").html();
var regex = /<br\s*[\/]?>/gi;
$("#content").html(str.replace(regex, "</p><br /><p>"));
$('br').prev('p').addClass('br');
$('br').next('p').addClass('indent');
removeEmptyNodes('#content p');
$('br').next('.scroller').children('p').first().addClass('indent');
$('br').replaceWith('');
removeEmptyNodes('#content p');
};
Edit:
My goal is that I have a paragraph with one or several line breaks. Like this simple case: <p>with some text <br />and another line<p>. I want the text after the line breaks to be indented and to be in a p of their own. So I need to split my original p. I don't want to add in divs or anything else nested in the original paragraphs. I need a bunch of sibling p tags at the end like this: <p class="br">with some text</p><p class="indent">and another line<p>
By which way I replace the <br />'s to split the p's does not matter to me...
Why don't you just find all without a regex?
$('p').each(function(){
var brs = $('br', this); //all <br>s withing this <p>
//do something with brs
});
Fiddle: http://jsfiddle.net/maniator/Ra8ax/
UPDATE:
Here is what you want with your new spec:
$('p').each(function(){
var html = this.innerHTML;
var htmlArray = html.split('<br>');
var new_html = htmlArray[0];
for(var i = 1; i < htmlArray.length; i++){
new_html += "<div class='break'>"+htmlArray[i]+"</div>";
}
this.innerHTML = new_html;
});
Fiddle: http://jsfiddle.net/maniator/Ra8ax/8/
UPDATE with no nesting:
JS:
$('p').each(function(){
var html = this.innerHTML;
var htmlArray = html.split('<br>');
var new_html = "<p>"+htmlArray[0]+"</p>";
for(var i = 1; i < htmlArray.length; i++){
new_html += "<p class='break'>"+htmlArray[i]+"</p>";
}
$(this).replaceWith(new_html);
});
Fiddle: http://jsfiddle.net/maniator/Ra8ax/11/
Use a Jquery selector instead of regex: $('p br')
Update 2:
$('#content')
.contents()
.filter(function() {
return this.nodeType == Node.TEXT_NODE;
})
.wrap("<p>");
var br = $('#content br');
br.prev('p').addClass('br');
br.next('p').addClass('indent');
br.remove();
http://jsfiddle.net/wWsht/

Categories