Javascript replace an element with a string - javascript

How can you change this
<div id='myDiv'><p>This div has <span>other elements</span> in it.</p></div>
into this
<div id='myDiv'>This div has other elements in it.</div>
hopefully using something like this
var ele = document.getElementById('myDiv');
while(ele.firstChild) {
replaceFunction(ele.firstChild, ele.firstChild.innerHTML);
}
function replaceFunction(element, text) {
// CODE TO REPLACE ELEMENT WITH TEXT
}

You can use innerText and textContent if you want to remove all descendant nodes, but leave the text:
// Microsoft
ele.innerText = ele.innerText;
// Others
ele.textContent = ele.textContent;
If you only want to flatten certain ones, you can define:
function foldIntoParent(element) {
while(ele.firstChild) {
ele.parentNode.insertBefore(ele.firstChild, ele);
}
ele.parentNode.removeChild(ele);
}
should pull all the children out of ele into its parent, and remove ele.
So if ele is the span above, it will do what you want. To find and fold all the element children of #myDiv do this:
function foldAllElementChildren(ele) {
for (var child = ele.firstChild, next; child; child = next) {
next = child.nextSibling;
if (child.nodeType === 1 /* ELEMENT */) {
foldIntoParent(child);
}
}
}
foldAllElementChildren(document.getElementById('myDiv'));

If you're not opposed to using jQuery, stripping the HTML from an element and leaving only the text is as simple as:
$(element).html($(element).text());

You can just take the innerText
console.log(ele.innerText)

Related

Jquery remove the innertext but preserve the html

I have something like this.
<div id="firstDiv">
This is some text
<span id="firstSpan">First span text</span>
<span id="secondSpan">Second span text</span>
</div>
I want to remove 'This is some text' and need the html elements intact.
I tried using something like
$("#firstDiv")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text("");
But it didn't work.
Is there a way to get (and possibly remove, via something like .text("")) just the free text within a tag, and not the text within its child tags?
Thanks very much.
Filter out text nodes and remove them:
$('#firstDiv').contents().filter(function() {
return this.nodeType===3;
}).remove();
FIDDLE
To also filter on the text itself, you can do:
$('#firstDiv').contents().filter(function() {
return this.nodeType === 3 && this.nodeValue.trim() === 'This is some text';
}).remove();
and to get the text :
var txt = [];
$('#firstDiv').contents().filter(function() {
if ( this.nodeType === 3 ) txt.push(this.nodeValue);
return this.nodeType === 3;
}).remove();
Check out this fiddle
Suppose you have this html
<parent>
<child>i want to keep the child</child>
Some text I want to remove
<child>i want to keep the child</child>
<child>i want to keep the child</child>
</parent>
Then you can remove the parent's inner text like this:
var child = $('parent').children('child');
$('parent').html(child);
Check this fiddle for a solution to your html
var child = $('#firstDiv').children('span');
$('#firstDiv').html(child);
PS: Be aware that any event handlers bounded on that div will be lost as you delete and then recreate the elements
Why try to force jQuery to do it when it's simpler with vanilla JS:
var div = document.getElementById('firstDiv'),
i,
el;
for (i = 0; i< div.childNodes.length; i++) {
el = div.childNodes[i];
if (el.nodeType === 3) {
div.removeChild(el);
}
}
Fiddle here: http://jsfiddle.net/YPKGQ/
Check this out, not sure if it does what you want exactly... Note: i only tested it in chrome
http://jsfiddle.net/LgyJ8/
cleartext($('#firstDiv'));
function cleartext(node) {
var children = $(node).children();
if(children.length > 0) {
var newhtml = "";
children.each(function() {
cleartext($(this));
newhtml += $('<div/>').append(this).html();
});
$(node).html(newhtml);
}
}

jQuery .text('') on multiple nested elements

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);

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".

How to select a part of string?

How to select a part of string?
My code (or example):
<div>some text</div>
$(function(){
$('div').each(function(){
$(this).text($(this).html().replace(/text/, '<span style="color: none">$1<\/span>'));
});
});
I tried this method, but in this case is selected all context too:
$(function(){
$('div:contains("text")').css('color','red');
});
I try to get like this:
<div><span style="color: red">text</span></div>
$('div').each(function () {
$(this).html(function (i, v) {
return v.replace(/foo/g, '<span style="color: red">$&<\/span>');
});
});
What are you actually trying to do? What you're doing at the moment is taking the HTML of each matching DIV, wrapping a span around the word "text" if it appears (literally the word "text") and then setting that as the text of the element (and so you'll see the HTML markup on the page).
If you really want to do something with the actual word "text", you probably meant to use html rather than text in your first function call:
$('div').each(function(){
$(this).html($(this).html().replace(/text/, '<span style="color: none">$1<\/span>'));
// ^-- here
}
But if you're trying to wrap a span around the text of the div, you can use wrap to do that:
$('div').wrap('<span style="color: none"/>');
Like this: http://jsbin.com/ucopo3 (in that example, I've used "color: blue" rather than "color: none", but you get the idea).
$(function(){
$('div:contains("text")').each(function() {
$(this).html($(this).html().replace(/(text)/g, '<span style="color:red;">\$1</span>'));
});
});
I've updated your fiddle: http://jsfiddle.net/nMzTw/15/
The general practice of interacting with the DOM as strings of HTML using innerHTML has many serious drawbacks:
Event handlers are removed or replaced
Opens the possibility of script inject attacks
Doesn't work in XHTML
It also encourages lazy thinking. In this particular instance, you're matching against the string "text" within the HTML with the assumption that any occurrence of the string must be within a text node. This is patently not a valid assumption: the string could appear in a title or alt attribute, for example.
Use DOM methods instead. This will get round all the problems. The following will use only DOM methods to surround every match for regex in every text node that is a descendant of a <div> element:
$(function() {
var regex = /text/;
function getTextNodes(node) {
if (node.nodeType == 3) {
return [node];
} else {
var textNodes = [];
for (var n = node.firstChild; n; n = n.nextSibling) {
textNodes = textNodes.concat(getTextNodes(n));
}
return textNodes;
}
}
$('div').each(function() {
$.each(getTextNodes(this), function() {
var textNode = this, parent = this.parentNode;
var result, span, matchedTextNode, matchLength;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchLength = result[0].length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
span = document.createElement("span");
span.style.color = "red";
span.appendChild(matchedTextNode);
parent.insertBefore(span, textNode);
}
});
});
});

How to get elements which have no children, but may have text?

the empty selector says that: Matches all elements that have no children (including text nodes).Finds all elements that are empty - they don't have child elements or text.
What i want is to get elements which has no children but may have text inside., how?
UPDATE:
Example:
I want select these elements which has no children but may have text, with syntax like this:
$('div:empty, a:empty, span, p:empty, td:empty, img, input').mousemove(myMouseOverHandler);
Get any element that doesn't have any other element:
$('*:not(:has(*))');
If an element has only text, children() will have a length of 0:
<div id="test1">
Hello World
</div>
<div id="test2">
<b>Hey there</b>
</div>
<script>
alert($("#test1").children().length); // alerts 0
alert($("#test2").children().length); // alerts 1 (the bold tag)
</script>
EDIT: In response to your edit, jQuery is awesome enough to let you do custom filters:
$.expr[':'].emptyOrText = function(e) {
return $(e).children().length == 0;
};
So, using the above against the HTML above, you could do this:
$('div:emptyOrText'); // will select #test1
I made a pure JavaScript function for anyone that does not want to use jQuery.
const getElementsWithNoChildren = (target) => {
let candidates;
if (target && typeof target.querySelectorAll === 'function') {
candidates = target.querySelectorAll('*');
}
else if (target && typeof target.length === 'number') {
candidates = target;
}
else {
candidates = document.querySelectorAll('*');
}
return Array.from(candidates).filter((elem) => {
return elem.children.length === 0;
});
};

Categories

Resources