Replace all the ocurrance of a string in an element - javascript

I want to replace a particular string in (the text of) all the descendant elements of a given element.
innerHTML cannot be used as this sequence can appear in attributes. I have tried using XPath, but it seems the interface is essentially read-only. Because this is limited to one element, functions like document.getElementsByTagName cannot be used either.
Could any suggest any way to do this? Any jQuery or pure DOM method is acceptable.
Edit:
Some of the answers are suggesting the problem I was trying to work around: modifying the text directly on an Element will cause all non-Text child nodes to be removed.
So the problem essentially comes down to how to efficiently select all the Text nodes in a tree. In XPath, you can easily do it as //text(), but the current XPath interface does not allow you to change these Text nodes it seems.
One way to do this is by recursion as shown in the answer by Bergi. Another way is to use the find('*') selector of jQuery, but this is a bit more expensive. Still waiting to see if there' are better solutions.

Just use a simple selfmade DOM-iterator, which walks recursively over all nodes:
(function iterate_node(node) {
if (node.nodeType === 3) { // Node.TEXT_NODE
var text = node.data.replace(/any regular expression/g, "any replacement");
if (text != node.data) // there's a Safari bug
node.data = text;
} else if (node.nodeType === 1) { // Node.ELEMENT_NODE
for (var i = 0; i < node.childNodes.length; i++) {
iterate_node(node.childNodes[i]); // run recursive on DOM
}
}
})(content); // any dom node

A solution might be to surf through all available nodes (TextNodes included) and apply a regexp pattern on the results. To grab TextNodes as well, you need to invoke jQuerys .contents(). For instance:
var search = "foo",
replaceWith = 'bar',
pattern = new RegExp( search, 'g' );
function searchReplace( root ) {
$( root ).contents().each(function _repl( _, node ) {
if( node.nodeType === 3 )
node.nodeValue = node.nodeValue.replace( pattern, replaceWith );
else searchReplace( node );
});
}
$('#apply').on('click', function() {
searchReplace( document.getElementById('rootNode') );
});
Example: http://jsfiddle.net/h8Rxu/3/
Reference: .contents()

Using jQuery:
$('#parent').children().each(function () {
var that = $(this);
that.text(that.text().replace('test', 'foo'));
});
If you prefer to search through all children instead of just immediate children, use .find() instead.
http://jsfiddle.net/ExwDx/
Edit: Documentation for children, each, text, and find.

Sorry, just got it myself:
$('#id').find('*').each(function(){
$.each(this.childNodes, function() {
if (this.nodeType === 3) {
this.data = this.data.toUpperCase();
}
})
})
I used toUpperCase() here to make the result more obvious, but any String operation would be valid there.

Related

Scan the DOM upward to find element matching selector (no jQuery)

I wanted a function that could scan the DOM upward from a DOMElement and also scan the children of each parent as it's going up.
It had to keep going until it would found any <element> matching the selector received in parameter. The selector had to be any type of valid CSS selector.
It was also needed to be done in pure JS (no jQuery)
I ended up making a modified version of this function that I found on this site. You can use this function as you wish, scale it up, claim it yours, whatever you want.
Here's the solution I found
GetClosest = function (elem, selector) {
for (; elem && elem !== document.body; elem = elem.parentNode) {
// If the elem matches at first iteration.
if(elem.matches(selector)) {
return elem;
} else {
// Scans all the childs of current iterated element (always higher in DOM until found).
// If one matches the selector it'll stop and return it.
child = elem.parentNode.firstChild;
do {
if(child.nodeType === 3) continue; // text node
if(child.matches(selector)) return child;
} while (child = child.nextElementSibling);
}
}
return null;
};

Modifying a string of html in javascript [duplicate]

I'm trying to get the HTML of a selected object with jQuery. I am aware of the .html() function; the issue is that I need the HTML including the selected object (a table row in this case, where .html() only returns the cells inside the row).
I've searched around and found a few very ‘hackish’ type methods of cloning an object, adding it to a newly created div, etc, etc, but this seems really dirty. Is there any better way, or does the new version of jQuery (1.4.2) offer any kind of outerHtml functionality?
I believe that currently (5/1/2012), all major browsers support the outerHTML function. It seems to me that this snippet is sufficient. I personally would choose to memorize this:
// Gives you the DOM element without the outside wrapper you want
$('.classSelector').html()
// Gives you the outside wrapper as well only for the first element
$('.classSelector')[0].outerHTML
// Gives you the outer HTML for all the selected elements
var html = '';
$('.classSelector').each(function () {
html += this.outerHTML;
});
//Or if you need a one liner for the previous code
$('.classSelector').get().map(function(v){return v.outerHTML}).join('');
EDIT: Basic support stats for element.outerHTML
Firefox (Gecko): 11 ....Released 2012-03-13
Chrome: 0.2 ...............Released 2008-09-02
Internet Explorer 4.0...Released 1997
Opera 7 ......................Released 2003-01-28
Safari 1.3 ...................Released 2006-01-12
No need to generate a function for it. Just do it like this:
$('a').each(function(){
var s = $(this).clone().wrap('<p>').parent().html();
console.log(s);
});
(Your browser's console will show what is logged, by the way. Most of the latest browsers since around 2009 have this feature.)
The magic is this on the end:
.clone().wrap('<p>').parent().html();
The clone means you're not actually disturbing the DOM. Run it without it and you'll see p tags inserted before/after all hyperlinks (in this example), which is undesirable. So, yes, use .clone().
The way it works is that it takes each a tag, makes a clone of it in RAM, wraps with p tags, gets the parent of it (meaning the p tag), and then gets the innerHTML property of it.
EDIT: Took advice and changed div tags to p tags because it's less typing and works the same.
2014 Edit : The question and this reply are from 2010. At the time, no better solution was widely available. Now, many of the other replies are better : Eric Hu's, or Re Capcha's for example.
This site seems to have a solution for you :
jQuery: outerHTML | Yelotofu
jQuery.fn.outerHTML = function(s) {
return s
? this.before(s).remove()
: jQuery("<p>").append(this.eq(0).clone()).html();
};
What about: prop('outerHTML')?
var outerHTML_text = $('#item-to-be-selected').prop('outerHTML');
And to set:
$('#item-to-be-selected').prop('outerHTML', outerHTML_text);
It worked for me.
PS: This is added in jQuery 1.6.
Extend jQuery:
(function($) {
$.fn.outerHTML = function() {
return $(this).clone().wrap('<div></div>').parent().html();
};
})(jQuery);
And use it like this: $("#myTableRow").outerHTML();
I agree with Arpan (Dec 13 '10 5:59).
His way of doing it is actually a MUCH better way of doing it, as you dont use clone. The clone method is very time consuming, if you have child elements, and nobody else seemed to care that IE actually HAVE the outerHTML attribute (yes IE actually have SOME useful tricks up its sleeve).
But I would probably create his script a bit different:
$.fn.outerHTML = function() {
var $t = $(this);
if ($t[0].outerHTML !== undefined) {
return $t[0].outerHTML;
} else {
var content = $t.wrap('<div/>').parent().html();
$t.unwrap();
return content;
}
};
To be truly jQuery-esque, you might want outerHTML() to be a getter and a setter and have its behaviour as similar to html() as possible:
$.fn.outerHTML = function (arg) {
var ret;
// If no items in the collection, return
if (!this.length)
return typeof arg == "undefined" ? this : null;
// Getter overload (no argument passed)
if (!arg) {
return this[0].outerHTML ||
(ret = this.wrap('<div>').parent().html(), this.unwrap(), ret);
}
// Setter overload
$.each(this, function (i, el) {
var fnRet,
pass = el,
inOrOut = el.outerHTML ? "outerHTML" : "innerHTML";
if (!el.outerHTML)
el = $(el).wrap('<div>').parent()[0];
if (jQuery.isFunction(arg)) {
if ((fnRet = arg.call(pass, i, el[inOrOut])) !== false)
el[inOrOut] = fnRet;
}
else
el[inOrOut] = arg;
if (!el.outerHTML)
$(el).children().unwrap();
});
return this;
}
Working demo: http://jsfiddle.net/AndyE/WLKAa/
This allows us to pass an argument to outerHTML, which can be
a cancellable function — function (index, oldOuterHTML) { } — where the return value will become the new HTML for the element (unless false is returned).
a string, which will be set in place of the HTML of each element.
For more information, see the jQuery docs for html().
You can also use get (Retrieve the DOM elements matched by the jQuery object.).
e.g:
$('div').get(0).outerHTML;//return "<div></div>"
As extension method :
jQuery.fn.outerHTML = function () {
return this.get().map(function (v) {
return v.outerHTML
}).join()
};
Or
jQuery.fn.outerHTML = function () {
return $.map(this.get(), function (v) {
return v.outerHTML
}).join()
};
Multiple choice and return the outer html of all matched elements.
$('input').outerHTML()
return:
'<input id="input1" type="text"><input id="input2" type="text">'
To make a FULL jQuery plugin as .outerHTML, add the following script to any js file and include after jQuery in your header:
update New version has better control as well as a more jQuery Selector friendly service! :)
;(function($) {
$.extend({
outerHTML: function() {
var $ele = arguments[0],
args = Array.prototype.slice.call(arguments, 1)
if ($ele && !($ele instanceof jQuery) && (typeof $ele == 'string' || $ele instanceof HTMLCollection || $ele instanceof Array)) $ele = $($ele);
if ($ele.length) {
if ($ele.length == 1) return $ele[0].outerHTML;
else return $.map($("div"), function(ele,i) { return ele.outerHTML; });
}
throw new Error("Invalid Selector");
}
})
$.fn.extend({
outerHTML: function() {
var args = [this];
if (arguments.length) for (x in arguments) args.push(arguments[x]);
return $.outerHTML.apply($, args);
}
});
})(jQuery);
This will allow you to not only get the outerHTML of one element, but even get an Array return of multiple elements at once! and can be used in both jQuery standard styles as such:
$.outerHTML($("#eleID")); // will return outerHTML of that element and is
// same as
$("#eleID").outerHTML();
// or
$.outerHTML("#eleID");
// or
$.outerHTML(document.getElementById("eleID"));
For multiple elements
$("#firstEle, .someElesByClassname, tag").outerHTML();
Snippet Examples:
console.log('$.outerHTML($("#eleID"))'+"\t", $.outerHTML($("#eleID")));
console.log('$("#eleID").outerHTML()'+"\t\t", $("#eleID").outerHTML());
console.log('$("#firstEle, .someElesByClassname, tag").outerHTML()'+"\t", $("#firstEle, .someElesByClassname, tag").outerHTML());
var checkThisOut = $("div").outerHTML();
console.log('var checkThisOut = $("div").outerHTML();'+"\t\t", checkThisOut);
$.each(checkThisOut, function(i, str){ $("div").eq(i).text("My outerHTML Was: " + str); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://rawgit.com/JDMcKinstry/ce699e82c7e07d02bae82e642fb4275f/raw/deabd0663adf0d12f389ddc03786468af4033ad2/jQuery.outerHTML.js"></script>
<div id="eleID">This will</div>
<div id="firstEle">be Replaced</div>
<div class="someElesByClassname">At RunTime</div>
<h3><tag>Open Console to see results</tag></h3>
you can also just do it this way
document.getElementById(id).outerHTML
where id is the id of the element that you are looking for
I used Jessica's solution (which was edited by Josh) to get outerHTML to work on Firefox. The problem however is that my code was breaking because her solution wrapped the element into a DIV. Adding one more line of code solved that problem.
The following code gives you the outerHTML leaving the DOM tree unchanged.
$jq.fn.outerHTML = function() {
if ($jq(this).attr('outerHTML'))
return $jq(this).attr('outerHTML');
else
{
var content = $jq(this).wrap('<div></div>').parent().html();
$jq(this).unwrap();
return content;
}
}
And use it like this: $("#myDiv").outerHTML();
Hope someone finds it useful!
// no cloning necessary
var x = $('#xxx').wrapAll('<div></div>').parent().html();
alert(x);
Fiddle here: http://jsfiddle.net/ezmilhouse/Mv76a/
If the scenario is appending a new row dynamically, you can use this:
var row = $(".myRow").last().clone();
$(".myRow").last().after(row);
.myrow is the classname of the <tr>. It makes a copy of the last row and inserts that as a new last row.
This also works in IE7, while the [0].outerHTML method does not allow assignments in ie7
node.cloneNode() hardly seems like a hack. You can clone the node and append it to any desired parent element, and also manipulate it by manipulating individual properties, rather than having to e.g. run regular expressions on it, or add it in to the DOM, then manipulate it afterwords.
That said, you could also iterate over the attributes of the element to construct an HTML string representation of it. It seems likely this is how any outerHTML function would be implemented were jQuery to add one.
I've used Volomike's solution updated by Jessica. Just added a check to see if the element exists, and made it return blank in case it doesn't.
jQuery.fn.outerHTML = function() {
return $(this).length > 0 ? $(this).clone().wrap('<div />').parent().html() : '';
};
Of course, use it like:
$('table#buttons').outerHTML();
You can find a good .outerHTML() option here https://github.com/darlesson/jquery-outerhtml.
Unlike .html() that returns only the element's HTML content, this version of .outerHTML() returns the selected element and its HTML content or replaces it as .replaceWith() method but with the difference that allows the replacing HTML to be inherit by the chaining.
Examples can also be seeing in the URL above.
This is quite simple with vanilla JavaScript...
document.querySelector('#selector')
Note that Josh's solution only works for a single element.
Arguably, "outer" HTML only really makes sense when you have a single element, but there are situations where it makes sense to take a list of HTML elements and turn them into markup.
Extending Josh's solution, this one will handle multiple elements:
(function($) {
$.fn.outerHTML = function() {
var $this = $(this);
if ($this.length>1)
return $.map($this, function(el){ return $(el).outerHTML(); }).join('');
return $this.clone().wrap('<div/>').parent().html();
}
})(jQuery);
Edit: another problem with Josh's solution fixed, see comment above.
Anothe similar solution with added remove() of the temporary DOM object.
I have made this simple test with outerHTML being tokimon solution (without clone), and outerHTML2 being jessica solution (clone)
console.time("outerHTML");
for(i=0;i<1000;i++)
{
var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML();
}
console.timeEnd("outerHTML");
console.time("outerHTML2");
for(i=0;i<1000;i++)
{
var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML2();
}
console.timeEnd("outerHTML2");
and the result in my chromium (Version 20.0.1132.57 (0)) browser was
outerHTML: 81ms
outerHTML2: 439ms
but if we use tokimon solution without the native outerHTML function (which is now supported in probably almost every browser)
we get
outerHTML: 594ms
outerHTML2: 332ms
and there are gonna be more loops and elements in real world examples, so the perfect combination would be
$.fn.outerHTML = function()
{
$t = $(this);
if( "outerHTML" in $t[0] ) return $t[0].outerHTML;
else return $t.clone().wrap('<p>').parent().html();
}
so clone method is actually faster than wrap/unwrap method
(jquery 1.7.2)
Here is a very optimized outerHTML plugin for jquery:
(http://jsperf.com/outerhtml-vs-jquery-clone-hack/5 => the 2 others fast code snippets are not compatible with some browsers like FF < 11)
(function($) {
var DIV = document.createElement("div"),
outerHTML;
if ('outerHTML' in DIV) {
outerHTML = function(node) {
return node.outerHTML;
};
} else {
outerHTML = function(node) {
var div = DIV.cloneNode();
div.appendChild(node.cloneNode(true));
return div.innerHTML;
};
}
$.fn.outerHTML = function() {
return this.length ? outerHTML(this[0]) : void(0);
};
})(jQuery);
#Andy E => I don't agree with you. outerHMTL doesn't need a getter AND a setter: jQuery already give us 'replaceWith'...
#mindplay => Why are you joining all outerHTML? jquery.html return only the HTML content of the FIRST element.
(Sorry, don't have enough reputation to write comments)
Short and sweet.
[].reduce($('.x'), function(i,v) {return i+v.outerHTML}, '')
or event more sweet with help of arrow functions
[].reduce.call($('.x'), (i,v) => i+v.outerHTML, '')
or without jQuery at all
[].reduce.call(document.querySelectorAll('.x'), (i,v) => i+v.outerHTML, '')
or if you don't like this approach, check that
$('.x').get().reduce((i,v) => i+v.outerHTML, '')
This is great for changing elements on the dom but does NOT work for ie when passing in a html string into jquery like this:
$('<div id="foo">Some <span id="blog">content</span></div>').find('#blog').outerHTML();
After some manipulation I have created a function which allows the above to work in ie for html strings:
$.fn.htmlStringOuterHTML = function() {
this.parent().find(this).wrap('<div/>');
return this.parent().html();
};
$.html = el => $("<div>"+el+"</div>").html().trim();
I came across this while looking for an answer to my issue which was that I was trying to remove a table row then add it back in at the bottom of the table (because I was dynamically creating data rows but wanted to show an 'Add New Record' type row at the bottom).
I had the same issue, in that it was returning the innerHtml so was missing the TR tags, which held the ID of that row and meant it was impossible to repeat the procedure.
The answer I found was that the jquery remove() function actually returns the element, that it removes, as an object. So, to remove and re-add a row it was as simple as this...
var a = $("#trRowToRemove").remove();
$('#tblMyTable').append(a);
If you're not removing the object but want to copy it somewhere else, use the clone() function instead.
jQuery plugin as a shorthand to directly get the whole element HTML:
jQuery.fn.outerHTML = function () {
return jQuery('<div />').append(this.eq(0).clone()).html();
};
And use it like this: $(".element").outerHTML();
Pure JavaScript:
var outerHTML = function(node) {
var div = document.createElement("div");
div.appendChild(node.cloneNode(true));
return div.innerHTML;
};
$("#myNode").parent(x).html();
Where 'x' is the node number, beginning with 0 as the first one, should get the right node you want, if you're trying to get a specific one. If you have child nodes, you should really be putting an ID on the one you want, though, to just zero in on that one. Using that methodology and no 'x' worked fine for me.
Simple solution.
var myself = $('#div').children().parent();
$("#myTable").parent().html();
Perhaps I'm not understanding your question properly, but this will get the selected element's parent element's html.
Is that what you're after?

How to access innerHTML but ignore <script> tags

I have a plugin that looks through the HTML and replaces text. However, with my current implementation text in script tags are getting caught in the search as well. This leads to broken scripts on the affected pages.
var pageText = document.body.innerHTML;
document.body.innerHTML = pageText.replace(regextgoeshere);
I tried my best to filter it out through my regex pattern but I need to figure out how to skip all tags.
Is there a way to skip all tags when getting innerHTML?
Maybe your best option is to use querySelectorAll and negate undesired elements. Then replace the textContent instead innerHTML. By using innerHTML you risk breaking document tags.
This is a cross-browser solution.
var matches = document.querySelectorAll("*:not(html):not(head):not(script):not(meta):not(link)");
console.log(matches);
[].forEach.call(matches, function(elem) {
var text = ('innerText' in elem) ? 'innerText' : 'textContent';
elem[text] = elem[text].replace("this", "works");
});
http://jsfiddle.net/m6qhuesv/
Note 1: HTML, HEAD, META and LINK tags disallow modify textContext.
Note 2: innerText is a proprietary IE thing (also works in chrome). The W3C defines textContent as the official property.
EDIT: I misunderstood your requirements
If you want something more sophisticated, try Douglas Crockford's walking the DOM function:
function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
You can use the tagName property of node to skip <script> elements:
if(node.tagName.toLowerCase() !== 'script'){
node.innerText = node.innerText.replace(regextgoeshere);
}
I think we tend to think elements and miss nodes!
However this problem is best solved by thinking nodes.
Australian Alex has best solution
http://blog.alexanderdickson.com/javascript-replacing-text
function myRecursiveSearch(node,.....) {
var excludeElements = ['script', 'style', 'iframe', 'canvas'];
var child = node.firstChild;
if(child==null)
return;
do {
switch (child.nodeType) {
case 1:
if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) {
continue;
}
myRecursiveSearch(child,.....);
break;
case 3:
child.nodeValue=doReolace(child.nodeValue,.....);
break;
}
} while (child = child.nextSibling);
}
function doTranslit(strtext,....) {
.....
return strtext;
}
Didn't check but you can try.
var pageText = document.body.innerHTML;
mypagewithoutScriptTag = pageText.replace(<script>(.*?)</script>);

Find and replace specific text characters across a document with JS

I'm wondering if there is a lightweight way I could use JavaScript or jQuery to sniff out a specific text character across a document; say € and find all instances of this character. And then! Write an ability to replace all instances of this with say a $.
I found this snippet for starters:
var str = 'test: '';
str = str.replace(/'/g, "'");
Essentially; I am wanting a solution for a one page document. Grab all instances of X and make it XY. Only text characters.
How about this, replacing # with $:
$("body").children().each(function () {
$(this).html( $(this).html().replace(/#/g,"$") );
});
http://jsfiddle.net/maximua/jp96C/1/
ECMAScript 2015+ approach
Pitfalls when solving this task
This seems like an easy task, but you have to take care of several things:
Simply replacing the entire HTML (e.g. using innerHTML) causes the affected subtree of the DOM to be entirely deleted and replaced, however event listeners are attached to the existing, now deleted elements, so they’re deleted with them. Similarly, WeakMap entries for the existing elements will all be deleted. This is because all of these things need the exact references to the elements or nodes; a replaced innerHTML will create entirely new references and discard the old ones.
Replacing the HTML may also replace <script> or <style> contents, or HTML tag or attribute names, which is not always desired.
Changing the HTML may result in an xss attack.
You may want to replace attribute values, e.g. for title and alt, in a controlled manner as well, but those all-or-nothing approaches as well as regexes are ill-equipped to do so.
Guarding against xss attacks generally can’t be solved by using the approaches below. E.g. if a fetch call reads a URL from somewhere on the page, then sends a request to that URL, the functions below won’t stop that, since this scenario is inherently unsafe.
Replacing the text contents of all elements
This basically selects all elements that contain normal text, goes through their child nodes — among those are also text nodes —, seeks those text nodes out and replaces their contents.
You can optionally specify a different root target, e.g. replaceOnDocument(/€/g, "$", { target: someElement });; by default, the <body> is chosen.
const replaceOnDocument = (pattern, string, {target = document.body} = {}) => {
// Handle `string` — see the last section
[
target,
...target.querySelectorAll("*:not(script):not(noscript):not(style)")
].forEach(({childNodes: [...nodes]}) => nodes
.filter(({nodeType}) => nodeType === Node.TEXT_NODE)
.forEach((textNode) => textNode.textContent = textNode.textContent.replace(pattern, string)));
};
replaceOnDocument(/€/g, "$");
Replacing text nodes, element attributes and properties
Now, this is a little more complex: you need to check three cases: whether a node is a text node, whether it’s an element and its attribute should be replaced, or whether it’s an element and its property should be replaced. A replacer object provides methods for text nodes and for elements.
Before replacing attributes and properties, the replacer needs to check whether the element has a matching attribute; otherwise new attributes get created, undesirably. It also needs to check whether the targeted property is a string, since only strings can be replaced, or whether the matching property to the targeted attribute is not a function, since this may lead to an xss attack.
In the example below, you can see how to use the extended features: in the optional third argument, you may add an attrs property and a props property, which is an iterable (e.g. an array) each, for the attributes to be replaced and the properties to be replaced, respectively.
You’ll also notice that this snippet uses flatMap. If that’s not supported, use a polyfill or replace it by the reduce–concat, or map–reduce–concat construct, as seen in the linked documentation.
const replaceOnDocument = (() => {
const replacer = {
[Node.TEXT_NODE](node, pattern, string){
node.textContent = node.textContent.replace(pattern, string);
},
[Node.ELEMENT_NODE](node, pattern, string, {attrs, props} = {}){
attrs.forEach((attr) => {
if(typeof node[attr] !== "function" && node.hasAttribute(attr)){
node.setAttribute(attr, node.getAttribute(attr).replace(pattern, string));
}
});
props.forEach((prop) => {
if(typeof node[prop] === "string" && node.hasAttribute(prop)){
node[prop] = node[prop].replace(pattern, string);
}
});
}
};
return (pattern, string, {target = document.body, attrs: [...attrs] = [], props: [...props] = []} = {}) => {
// Handle `string` — see the last section
[
target,
...[
target,
...target.querySelectorAll("*:not(script):not(noscript):not(style)")
].flatMap(({childNodes: [...nodes]}) => nodes)
].filter(({nodeType}) => replacer.hasOwnProperty(nodeType))
.forEach((node) => replacer[node.nodeType](node, pattern, string, {
attrs,
props
}));
};
})();
replaceOnDocument(/€/g, "$", {
attrs: [
"title",
"alt",
"onerror" // This will be ignored
],
props: [
"value" // Changing an `<input>`’s `value` attribute won’t change its current value, so the property needs to be accessed here
]
});
Replacing with HTML entities
If you need to make it work with HTML entities like ­, the above approaches will just literally produce the string ­, since that’s an HTML entity and will only work when assigning .innerHTML or using related methods.
So let’s solve it by passing the input string to something that accepts an HTML string: a new, temporary HTMLDocument. This is created by the DOMParser’s parseFromString method; in the end we read its documentElement’s textContent:
string = new DOMParser().parseFromString(string, "text/html").documentElement.textContent;
If you want to use this, choose one of the approaches above, depending on whether or not you want to replace HTML attributes and DOM properties in addition to text; then simply replace the comment // Handle `string` — see the last section by the above line.
Now you can use replaceOnDocument(/Güterzug/g, "Güter­zug");.
NB: If you don’t use the string handling code, you may also remove the { } around the arrow function body.
Note that this parses HTML entities but still disallows inserting actual HTML tags, since we’re reading only the textContent. This is also safe against most cases of xss: since we’re using parseFromString and the page’s document isn’t affected, no <script> gets downloaded and no onerror handler gets executed.
You should also consider using \xAD instead of ­ directly in your JavaScript string, if it turns out to be simpler.
My own suggestion is as follows:
function nativeSelector() {
var elements = document.querySelectorAll("body, body *");
var results = [];
var child;
for(var i = 0; i < elements.length; i++) {
child = elements[i].childNodes[0];
if(elements[i].hasChildNodes() && child.nodeType == 3) {
results.push(child);
}
}
return results;
}
var textnodes = nativeSelector(),
_nv;
for (var i = 0, len = textnodes.length; i<len; i++){
_nv = textnodes[i].nodeValue;
textnodes[i].nodeValue = _nv.replace(/£/g,'€');
}
JS Fiddle demo.
The nativeSelector() function comes from an answer (posted by Anurag) to this question: getElementsByTagName() equivalent for textNodes.
I think you may be overthinking this.
My approach is simple.
Enclose you page with a div tag:
<div id="mydiv">
<!-- you page here -->
</div>
In your javascript:
var html=document.getElementById('mydiv').innerHTML;
html = html.replace(/this/g,"that");
document.getElementById('mydiv').innerHTML=html;
Similar to #max-malik's answer, but without using jQuery, you can also do this using document.createTreeWalker:
button.addEventListener('click', e => {
const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
while (treeWalker.nextNode()) {
const node = treeWalker.currentNode;
node.textContent = node.textContent.replace(/#/g, '$');
}
})
<div>This is an # that we are # replacing.</div>
<div>This is another # that we are replacing.</div>
<div>
<span>This is an # in a span in # div.</span>
</div>
<br>
<input id="button" type="button" value="Replace # with $" />
Vanilla JavaScript solution:
document.body.innerHTML = document.body.innerHTML.replace(/Original/g, "New")
The best would be to do this server-side or wrap the currency symbols in an element you can select before returning it to the browser, however if neither is an option, you can select all text nodes within the body and do the replace on them. Below i'm doing this using a plugin i wrote 2 years ago that was meant for highlighting text. What i'm doing is finding all occurrences of € and wrapping it in a span with the class currency-symbol, then i'm replacing the text of those spans.
Demo
(function($){
$.fn.highlightText = function () {
// handler first parameter
// is the first parameter a regexp?
var re,
hClass,
reStr,
argType = $.type(arguments[0]),
defaultTagName = $.fn.highlightText.defaultTagName;
if ( argType === "regexp" ) {
// first argument is a regular expression
re = arguments[0];
}
// is the first parameter an array?
else if ( argType === "array" ) {
// first argument is an array, generate
// regular expression string for later use
reStr = arguments[0].join("|");
}
// is the first parameter a string?
else if ( argType === "string" ) {
// store string in regular expression string
// for later use
reStr = arguments[0];
}
// else, return out and do nothing because this
// argument is required.
else {
return;
}
// the second parameter is optional, however,
// it must be a string or boolean value. If it is
// a string, it will be used as the highlight class.
// If it is a boolean value and equal to true, it
// will be used as the third parameter and the highlight
// class will default to "highlight". If it is undefined,
// the highlight class will default to "highlight" and
// the third parameter will default to false, allowing
// the plugin to match partial matches.
// ** The exception is if the first parameter is a regular
// expression, the third parameter will be ignored.
argType = $.type(arguments[1]);
if ( argType === "string" ) {
hClass = arguments[1];
}
else if ( argType === "boolean" ) {
hClass = "highlight";
if ( reStr ) {
reStr = "\\b" + reStr + "\\b";
}
}
else {
hClass = "highlight";
}
if ( arguments[2] && reStr ) {
reStr = reStr = "\\b" + reStr + "\\b";
}
// if re is not defined ( which means either an array or
// string was passed as the first parameter ) create the
// regular expression.
if (!re) {
re = new RegExp( "(" + reStr + ")", "ig" );
}
// iterate through each matched element
return this.each( function() {
// select all contents of this element
$( this ).find( "*" ).andSelf().contents()
// filter to only text nodes that aren't already highlighted
.filter( function () {
return this.nodeType === 3 && $( this ).closest( "." + hClass ).length === 0;
})
// loop through each text node
.each( function () {
var output;
output = this.nodeValue
.replace( re, "<" + defaultTagName + " class='" + hClass + "'>$1</" + defaultTagName +">" );
if ( output !== this.nodeValue ) {
$( this ).wrap( "<p></p>" ).parent()
.html( output ).contents().unwrap();
}
});
});
};
$.fn.highlightText.defaultTagName = "span";
})( jQuery );
$("body").highlightText("€","currency-symbol");
$("span.currency-symbol").text("$");
Use split and join method
$("#idBut").click(function() {
$("body").children().each(function() {
$(this).html($(this).html().split('#').join("$"));
});
});
here is solution
In javascript without using jquery:
document.body.innerText = document.body.innerText.replace('actualword', 'replacementword');
You can use:
str.replace(/text/g, "replaced text");
For each element inside document body modify their text using .text(fn) function.
$("body *").text(function() {
return $(this).text().replace("x", "xy");
});
As you'll be using jQuery anyway, try:
https://github.com/cowboy/jquery-replacetext
Then just do
$("p").replaceText("£", "$")
It seems to do good job of only replacing text and not messing with other elements
str.replace(/replacetext/g,'actualtext')
This replaces all instances of replacetext with actualtext
Here is something that might help someone looking for this answer:
The following uses jquery it searches the whole document and only replaces the text.
for example if we had
overpopulation
and we wanted to add a span with the class overpop around the word overpopulation
<span class="overpop">overpopulation</span>
we would run the following
$("*:containsIN('overpopulation')").filter(
function() {
return $(this).find("*:contains('" + str + "')").length == 0
}
).html(function(_, html) {
if (html != 'undefined') {
return html.replace(/(overpopulation)/gi, '<span class="overpop">$1</span>');
}
});
the search is case insensitive searches the whole document and only replaces the text portions in this case we are searching for the string 'overpopulation'
$.extend($.expr[":"], {
"containsIN": function(elem, i, match, array) {
return (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
}
});

Are there any text selector in jquery?

Are there any text selector in jquery ?
My Code
<anything>Hello World! Hello World!</anything>
Reslut Should be (Using Jquery)
<anything>Hello <span>World</span>! Hello <span>World</span>!</anything>
No. jQuery works primarily with elements and gives you very little for handling text.
To do a find-and-replace on text you will need to check each text node separately and do DOM splitText operations to take it apart when a match is found. For example:
function findText(element, pattern, callback) {
for (var childi= element.childNodes.length; childi-->0;) {
var child= element.childNodes[childi];
if (child.nodeType==1) {
var tag= child.tagName.toLowerCase();
if (tag!=='script' && tag!=='style' && tag!=='textarea')
findText(child, pattern, callback);
} else if (child.nodeType==3) {
var matches= [];
var match;
while (match= pattern.exec(child.data))
matches.push(match);
for (var i= matches.length; i-->0;)
callback.call(window, child, matches[i]);
}
}
}
findText(element, /\bWorld\b/g, function(node, match) {
var span= document.createElement('span');
node.splitText(match.index+match[0].length);
span.appendChild(node.splitText(match.index));
node.parentNode.insertBefore(span, node.nextSibling);
});
$('anything').html(function(i, v) {
return v.replace(/(World)/g, '<span>$1</span>');
});
The above snippet uses functionality added in jQuery 1.4.
Note: this solution is safe for elements containing only raw text (and no child elements).
You can do a regex replacement, etc for your simple case, but for a more general answer: no.
jQuery just doesn't provide much help when dealing with text nodes, it's designed primarily for dealing with element node types (nodeType == 1), not text node types (nodeType == 3)...so yes you can use it where it helps (e.g. .contents() and .filter()), but that won't be often since it's not the library's main purpose.

Categories

Resources