jQuery .text('') on multiple nested elements - javascript

I wanted to remove all text from html and print only tags. I Ended up writing this:
var html = $('html');
var elements = html.find('*');
elements.text('');
alert(html.html());
It only out prints <head></head><body></body>. Was not that suppose to print all tags. I've nearly 2000 tags in the html.

var elements = html.find('*');
elements.text('');
That says "find all elements below html, then empty them". That includes body and head. When they are emptied, there are no other elements on the page, so they are the only ones that appear in html's content.
If you really wnat to remove all text from the page and leave the elements, you'll have to do it with DOM methods:
html.find('*').each(function() { // loop over all elements
$(this).contents().each(function() { // loop through each element's child nodes
if (this.nodeType === 3) { // if the node is a text node
this.parentNode.removeChild(this); // remove it from the document
}
});
})

You just deleted everything from your dom:
$('html').find('*').text('');
This will set the text of all nodes inside the <html> to the empty string, deleting descendant elements - the only two nodes that are left are the two children of the root node, <head></head> and <body></body> with their empty text node children - exactly the result you got.
If you want to remove all text nodes, you should use this:
var html = document.documentElement;
(function recurse(el) {
for (var i=0; i<el.childNodes.length; i++) {
var child = el.childNodes[i];
if (child.nodeType == 3)
el.removeChild(child);
else
recurse(child);
}
})(html);
alert(html.outerHTML);

Try this instead
$(function(){
var elements = $(document).find("*");
elements.each(function(index, data){
console.log(data);
});
});
This will return all the html elements of page.

lonesomeday seems to have the right path, but you could also do some string rebuilding like this:
var htmlString=$('html').html();
var emptyHtmlString="";
var isTag=false;
for (i=0;i<htmlString.length;i++)
{
if(htmlString[i]=='<')
isTag=true;
if(isTag)
{
emptyHtmlString+=htmlString[i];
}
if(htmlString[i]=='>')
isTag=false;
}
alert(emptyHtmlString);

Related

select html element by its full html tag - JS

I am looking for a way to be able to select an HTML element by its tag, like:
document.querySelector("<div id='myDiv'\>hello world</div\>")
//instead of: document.querySelector("#myDiv")
However, this code returns an error. The code should return the HTML element.
Does anybody know a way to achieve this? (vanilla JS preferred)
It seems a bit odd that you wouldn't want to select element via ID. But regardless one way of selecting the element in your example would be to look for its innerHTML.
e.g
var div = document.getElementsByTagName('div');
for (var i=0;i<div.length;i++){
console.log(div[i].innerHTML)
if(div [i].innerHTML == 'hello world'){
var element = div[i].parentElement
console.log(element)
break;
}
}
You could use outerHTML to search for it, however this only works if the element has a parent element.
var els = Array.from(document.querySelector('body *')); //this selects all elements in the body
var el;
for(var i = 0; i < els.length; i++) {
if(els.outerHTML === "<div id='myDiv'\>hello world</div\>") {
el = els[i];
}
}
//Use the el variable for your element

Replace text with link with chrome extension

I am trying to replace text on a webpage with links. When I try this it just replaces the text with the tag and not a link. For example this code will replace "river" with:
asdf
This is what I have so far:
function handleText(textNode)
{
var v = textNode.nodeValue;
v = v.replace(/\briver\b/g, 'asdf');
textNode.nodeValue = v;
}
If all you wanted to do was change the text to other plain text, then you could change the contents of the text nodes directly. However, you are wanting to add an <a> element. For each <a> element you want to add, you are effectively wanting to add a child element. Text nodes can not have children. Thus, to do this you have to actually replace the text node with a more complicated structure. In doing so, you will want to make as little impact on the DOM as possible, in order to not disturb other scripts which rely on the current structure of the DOM. The simplest way to make little impact is to replace the text node with a <span> which contains the new text nodes (the text will split around the new <a>) and any new <a> elements.
The code below should do what you desire. It replaces the textNode with a <span> containing the new text nodes and the created <a> elements. It only makes the replacement when one or more <a> elements need to be inserted.
function handleTextNode(textNode) {
if(textNode.nodeName !== '#text'
|| textNode.parentNode.nodeName === 'SCRIPT'
|| textNode.parentNode.nodeName === 'STYLE'
) {
//Don't do anything except on text nodes, which are not children
// of <script> or <style>.
return;
}
let origText = textNode.textContent;
let newHtml=origText.replace(/\briver\b/g,'asdf');
//Only change the DOM if we actually made a replacement in the text.
//Compare the strings, as it should be faster than a second RegExp operation and
// lets us use the RegExp in only one place for maintainability.
if( newHtml !== origText) {
let newSpan = document.createElement('span');
newSpan.innerHTML = newHtml;
textNode.parentNode.replaceChild(newSpan,textNode);
}
}
//Testing: Walk the DOM of the <body> handling all non-empty text nodes
function processDocument() {
//Create the TreeWalker
let treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT,{
acceptNode: function(node) {
if(node.textContent.length === 0) {
//Alternately, could filter out the <script> and <style> text nodes here.
return NodeFilter.FILTER_SKIP; //Skip empty text nodes
} //else
return NodeFilter.FILTER_ACCEPT;
}
}, false );
//Make a list of the text nodes prior to modifying the DOM. Once the DOM is
// modified the TreeWalker will become invalid (i.e. the TreeWalker will stop
// traversing the DOM after the first modification).
let nodeList=[];
while(treeWalker.nextNode()){
nodeList.push(treeWalker.currentNode);
}
//Iterate over all text nodes, calling handleTextNode on each node in the list.
nodeList.forEach(function(el){
handleTextNode(el);
});
}
document.getElementById('clickTo').addEventListener('click',processDocument,false);
<input type="button" id="clickTo" value="Click to process"/>
<div id="testDiv">This text should change to a link -->river<--.</div>
The TreeWalker code was taken from my answer here.

Highlight all comma's in page with jquery

I have a very very simple question and i didn't find my answer.
I have a page that is using ajax and it gets update again and again in one div of it.
Now, i want to Highlight ALL the commas , of that div.
For example, they get red color.
How can i do it with this ajax page ?
I also wanted to try with this code, but i coudn't
$(document":contains(',')").css("color","red");
I just need to find all the commas in that div every second and give a style to them .
How to do it with jquery?
Don't know about jQuery, but it can be done with pure javascript. But it's not so easy actually.
tl;dr jsFiddle
This answer does not cause DOM revalidation and does not mess-up with javascript events!
First you need to loop through page content and replace every comma (or every character) with a <span> or other node so that you can give it individual CSS style. Let's start with getting textNodes:
HTMLElement.prototype.getTextNodes = function(recursive, uselist) {
var list = this.childNodes;
var nodes = uselist!=null?uselist:[];
for(var i=0,l=list.length; i<l;i++) {
if(list[i].nodeType==Node.TEXT_NODE) {
nodes.push(list[i]);
}
else if(recursive==true&&list[i].nodeType==1&&list[i].tagName.toLowerCase()!="script"&&list[i].tagName.toLowerCase()!="style") {
list[i].getTextNodes(true, nodes);
}
}
//console.log(nodes);
return nodes;
}
You'll now need to split the spans wherever the commas are:
/*Turn single text node into many spans containing single letters */
/* #param
textNode - HTMLTextNode element
highlight - the character to highlight
#return
null
*/
function replaceLetters(textNode, highlight) {
//Get the string contained in the text node
var text = textNode.data;
//Generate a container to contain text-node data
var container = document.createElement("span");
//Create another span for every single letter
var tinyNodes = [];
//Split the letters in spans
for(var i=0,l=text.length;i<l; i++) {
//skip whitespace
if(text[i].match(/^\s*$/)) {
container.appendChild(document.createTextNode(text[i]));
}
//Create a span with the letter
else {
//Create a span
var tiny = document.createElement("span");
//If the letter is our character
if(text[i]==highlight)
tiny.className = "highlighted";
tiny.innerHTML = text[i];
container.appendChild(tiny);
}
}
//replace text node with a span
textNode.parentNode.insertBefore(container, textNode);
textNode.parentNode.removeChild(textNode);
}
The function above was originaly used for animating all letters on a page (even when it was already loaded). You only need to change color of some of these.
If the functions above are defined, call this:
var nodes = document.getElementById("myDiv").getTextNodes(true);
for(var i=0, l=nodes.length; i<l; i++) {
replaceLetters(nodes[i], ",");
}
You need to wrap the commas with an HTML tag (such as <span>).
$(window).load(function() {
$('.target').each(function() {
var string = $(this).html();
$(this).html(string.replace(/,/g , '<span class="comma">,</span>'));
});
});
Here is an example:
http://jsfiddle.net/5mh6ja1L/
I don't know how to do it in jQuery but in pure JavaScript it would be something like this:
var el = document.getElementById("content");
el.innerHTML = el.innerHTML.replace(/,/g, "<b class='highlight'>,</b>");
demo: http://jsfiddle.net/f9xs0c79/
No need to do loop. You can just select the container where you want to replace and replace.
$("p").html(
$("p").html().replace(/,/g,"<span class='comma'>,</span>")
);
http://jsfiddle.net/1570ya75/2/

jQuery selector for an element that directly contains text?

I was able to get this partially working using the :contains selector, but my problem is if an element contains an element that contains the text it is still returned. For example:
$('div:contains("test")')
Will select both divs below:
<div>something else
<div>test</div>
</div>
fiddle: http://jsfiddle.net/TT7dR/
How can I select only divs that "directly" contain the text? Meaning that in the above example only the child div would be selected.
UPDATE:
Just to clarify, if I were searching for the text "something else" instead of "test" then I would like to only find the parent div.
$('div>:contains("test")') is not a general solution, it only works for your specific example. It still matches any element whose descendants contain the text test, as long as its parent is a div.
There is in fact currently no selector that will select only direct parents of text nodes containing your target text. To do it you would have to walk the DOM tree yourself checking each text node you find for the target text, or write a plugin to do the same. It'd be slow, but then not as slow as :contains already is (it's not a standard CSS selector so you don't get browser-native fast selector support).
Here's a plain DOM function you could use as a starting point. It might be improved to find text in adjacent (non-normalised) text nodes, or to hide it in a plugin/selector-extension.
function findElementsDirectlyContainingText(ancestor, text) {
var elements= [];
walk(ancestor);
return elements;
function walk(element) {
var n= element.childNodes.length;
for (var i= 0; i<n; i++) {
var child= element.childNodes[i];
if (child.nodeType===3 && child.data.indexOf(text)!==-1) {
elements.push(element);
break;
}
}
for (var i= 0; i<n; i++) {
var child= element.childNodes[i];
if (child.nodeType===1)
walk(child);
}
}
}
Just to complete the knowledge base. If you need to get all DOM elements within the body (not only DIVs) that contain specific text or characters you can use:
function getNodesThatContain(text) {
var textNodes = $(document).find(":not(iframe, script)")
.contents().filter(
function() {
return this.nodeType == 3
&& this.textContent.indexOf(text) > -1;
});
return textNodes.parent();
};
console.log(getNodesThatContain("test"));
Hope that helps.
jsfiddle: http://jsfiddle.net/85qEh/2/
Credits: DMoses
You might have to do an in-efficient query. Do not use this solution if someone finds a selector that manages to filter out child elements: http://viralpatel.net/blogs/2011/02/jquery-get-text-element-without-child-element.html
$("div:contains('test')")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.filter(":contains('test')")
edit: that snippet above is just to test the element, in implementation it would look more like this: http://jsfiddle.net/rkw79/TT7dR/6/
$("div:contains('test')").filter(function() {
return (
$(this).clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.filter(":contains('test')").length > 0)
}).css('border', 'solid 1px black');
try adding the greater than:
$('div>:contains("test")')
Finds specific element, but not parents
var elementsContainingText = ($(':contains("' + text + '")', target)).filter(function() {
return $(this).contents().filter(function() {return this.nodeType === 3 && this.nodeValue.indexOf(text) !== -1; }).length > 0;
});
This seems to work for me:
$('div >:contains("test")');
http://jsfiddle.net/TT7dR/1/
This forces the matched :contains selector to be a direct child of the <div> element
Try the following:
$("div>div:contains(test):only-of-type")
Add more alternative:
if($(selector).text().trim().length) {
var thetext = $(selector).contents().filter(function(){
return this.nodeType === 3;
}).text().trim();
console.log(thetext);
}
It will select the text only and remove any element with tag!
Reference
You can simply select the element that doesn't have your element
$('div:contains("test"):not(:has(> div))')
less code to write (but with a little limitation):
let selector = $('div:contains("test")');
selector.not(selector.has('div:contains("test")'))
Just use the jQuery function (.has) because the css :has is experimental:
https://developer.mozilla.org/en-US/docs/Web/CSS/:has#Browser_compatibility
Limitation:
When you have a structure like this:
<div>
<div>test</div>
test
</div>
Then only the inner div - Element will be found by this solution. This is because there is still an Element - Child of the div that :contains the string "test".

Parsing through DOM get all children and values

Container is a div i've added some basic HTML to.
The debug_log function is printing the following:
I'm in a span!
I'm in a div!
I'm in a
p
What happened to the rest of the text in the p tag ("aragraph tag!!"). I think I don't understand how exactly to walk through the document tree. I need a function that will parse the entire document tree and return all of the elements and their values. The code below is sort of a first crack at just getting all of the values displayed.
container.innerHTML = '<span>I\'m in a span! </span><div> I\'m in a div! </div><p>I\'m in a <span>p</span>aragraph tag!!</p>';
DEMO.parse_dom(container);
DEMO.parse_dom = function(ele)
{
var child_arr = ele.childNodes;
for(var i = 0; i < child_arr.length; i++)
{
debug_log(child_arr[i].firstChild.nodeValue);
DEMO.parse_dom(child_arr[i]);
}
}
Generally when traversing the DOM, you want to specify a start point. From there, check if the start point has childNodes. If it does, loop through them and recurse the function if they too have childNodes.
Here's some code that outputs to the console using the DOM form of these nodes (I used the document/HTML element as a start point). You'll need to run an if against window.console if you're allowing non-developers to load this page/code and using console:
recurseDomChildren(document.documentElement, true);
function recurseDomChildren(start, output)
{
var nodes;
if(start.childNodes)
{
nodes = start.childNodes;
loopNodeChildren(nodes, output);
}
}
function loopNodeChildren(nodes, output)
{
var node;
for(var i=0;i<nodes.length;i++)
{
node = nodes[i];
if(output)
{
outputNode(node);
}
if(node.childNodes)
{
recurseDomChildren(node, output);
}
}
}
function outputNode(node)
{
var whitespace = /^\s+$/g;
if(node.nodeType === 1)
{
console.log("element: " + node.tagName);
}else if(node.nodeType === 3)
{
//clear whitespace text nodes
node.data = node.data.replace(whitespace, "");
if(node.data)
{
console.log("text: " + node.data);
}
}
}
Example: http://jsfiddle.net/ee5X6/
In
<p>I\'m in a <span>p</span>aragraph tag!!</p>
you request the first child, which is the text node containing "I\'m in a".
The text "aragraph tag!!" is the third child, which is not logged.
Curiously, the last line containing "p" should never occur, because the span element is not a direct child of container.
I'm not sure it is what you need or if it is possible in your environment but jQuery can accomplish something similar quite easily. Here is a quick jQuery example that might work.
<html>
<head>
<script src="INCLUDE JQUERY HERE">
</script>
</head>
<body>
<span>
<span>I\'m in a span! </span><div> I\'m in a div! </div><p>I\'m in a <span>p</span>aragraph tag!!</p>
</span>
<script>
function traverse(elem){
$(elem).children().each(function(i,e){
console.log($(e).text());
traverse($(e));
});
}
traverse($("body").children().first());
</script>
</body>
<html>
Which gives the following console output:
I\'m in a span!
I\'m in a div!
I\'m in a paragraph tag!!
p

Categories

Resources