Why is replace() disrupting onclick()? - javascript

I am using replace to search my dynamically loaded webpage for this symbol Æ and replace it with ®. I found the code to do so in this question:
Find and replace specific text characters across a document with JS
$("body").children().each(function () {
$(this).html( $(this).html().replace(/Æ/g,"®") );
});
However, after I added this code, this function stopped working.
document.getElementById("backToClasses").onclick = function() {
console.log("Clicked");
};
Can anyone tell me why this might happen?

In the second chunk of code (which I assume runs first) you are locating an element in the DOM and assigning a value to a property of it.
The first chunk of code goes over the DOM and converts large chunks of it into HTML source code, it then modifies that source code, then it generates new DOM elements from it and replaces whatever was there before with them.
So the element with the ID backToClasses:
Gets a click handler
Is converted to HTML
Is destroyed
Is replaced by a new version created from its old HTML
The click handler was only ever on the DOM, so the new element doesn't have it.
If you are going to take this approach, then you should look at looping over just the text nodes in the document and dealing in text rather than HTML. To do that you'll need to recursively loop over the DOM and test the node type of each element.
It would be better to fix the underlying problem that you are hacking around though. It is almost certainly down to an incorrectly specified character encoding somewhere.
The W3C has some material on character encodings that might be helpful.

By doing this:
$("body").children().each(function () {
$(this).html( $(this).html().replace(/Æ/g,"®") );
});
You re-create all the HTML elements, so any events that you might have bound before are lost. Don't use the .html() function to replace text. And still, I'm not sure that this is the best way to replace a character.
This replace should be done server-side, not client side. By doing it client-side (in JavaScript) you can get into different problems like SEO (Google indexing your site with your badly encoded characters). If the characters are like this inside a file, simply replace them in that file, make sure to save the file with the right encoding.

Related

Event handler stuck after changing body.innerHTML

I would like to change some text in my body html with code below. Replacing works fine, but after that it seems to stuck, can't get out of function, so next parts of my script cannot be executed.
Nothing will happen after clicking .another-button. I think problem is with line that change body.innerHTML because when I delete this line everything works fine.
$(".do-replace-button").on("click", function () {
//some different code
document.body.innerHTML = document.body.innerHTML.replace(regExp, newWord);
}
$(".another-button").on("click", function (event) {
//some code that won't execute
}
You are effectively replacing the innerHTML of your <body> with something else, which is a string, the result of your replace(). That string is parsed and built into new DOM elements and the old ones are getting replaced by the new ones.
Hence, the events you bound on the old ones are gone, because the elements are gone. One way to solve this would be to:
a) bind on document itself:
$(document).on('click', '.do-replace-button', function() {
// whatever...
})
b) find another, less aggressive way to achieve whatever it is you are achieving with that replace.
Do note it's not only your events that get removed, but any event bound by any library/plugin you might have loaded and already initialized by the time your script runs. I strongly recommend against running replace on innerHTML, especially on <body>.
The call to document.body.innerHTML and the right hand assignment that comes after it is completely replacing the html inside the document, or more specifically, the parser is building a completely new node tree inside the body so all of your HTMLElement nodes that may have previously had event listeners assigned to them are no longer there. I recommend you go another route of only replacing the the HTML that matches your regex instead of the entire HTML contents of the body element. Or you could have a function that is called inside the first event-listener callback that fires after the replacement of the body HTML, that will re-initialize the event listeners.

How to convert from mixed HTML-string/DOM-elements to DOM-elements in Javascript?

I wish to implement the following Javascript function:
function AllToDom(partsArray)
{
// Magic!
}
// Called like:
var rowObject = AllToDom(['<tr>', tdElem1, tdElem2, '<td>XXX</td>', '<td>',
divContents,'</td></tr>']);
// Where tdElem1, tdElem2, divContents are DOM node objects.
The thing is I want it to work on any kinds of combinations of DOM nodes and HTML fragments. As long as it produces a valid HTML of course. Unclosed HTML tags and disallowed element combinations (like <table><div></div>) are allowed to have undefined behavior.
My first idea was to concatenate it all in a HTML string, except in place of DOM elements add a placeholder comment <!--SNOOPY-->. So the above would result in the following string:
<tr><!--SNOOPY--><!--SNOOPY--><td>XXX</td><td><!--SNOOPY--></td></tr>
This is already a valid piece of HTML, so next I create a <div>, assign this to innerHTML, gather the produced DOM nodes, and iterate through them and replace all <!--SNOOPY--> with the respective DOM element.
There are two flaws with this approach however:
Adding a <tr> as a child element to a <div> is invalid. I don't know if it might not break on some condition.
Internet Explorer 8 (the least version that I need to support) strips all comments when assigning to innerHTML.
Are there any workarounds? Is this possible at all?
Finally found an answer: jQuery has already done all the dirty work in their parseHTML() method. And I just happen to be using jQuery anyway, so good for me! :)
I checked what the magic was behind the scenes, and it's really pretty gruesome. First, they inspect the HTML (with regexs...) to see what parent tag they need to use, and then they have a workaround for IE8, which apparently it DOES preserve comment nodes - but only if they come after a text node. All comments before the first text node are lost. And some tags are affected this way too, which I had no idea about. And then there's half a dozen other workarounds for IE & Webkit problems that I've never even heard of.
So, I'm just going to leave it to them to do the right thing, because trying to reproduce that stuff would be madness.

Can I take HTML, loop through it to change elements, and display the results as plain text?

I'm trying to develop a script that will take user submitted HTML, loop through it to identify matching tags, make adjustments to those matched tags, and then spit out the resulting HTML as plain text that the user can copy. The end goal here is to replace all href's in a submission and replace them with different URL's.
So for example, this:
Link A
<a data-track="false" href="http://example.com/">Link B</a>
Link C
Becomes this:
Link A
<a data-track="false" href="http://example.com/">Link B</a>
Link C
My first thought was to take the submitted HTML from the <textarea> field and put it in a variable. At this point the HTML becomes a string and I was going to loop through it with a regex to find matching tags. My issue was that I needed to find all <a> tags that did NOT include the attribute data-track="false". And as far as I can tell that's impossible with regex since each link isn't going to be on its own line.
My second thought was to loop through it using jQuery where I could use something like this:
$("a:not([data-tracking='false'])");
But I can't use jQuery like this on a string, right? It needs to be in the DOM.
I'm unsure of the best way to go about doing this. Maybe another language would prove helpful, but other than HTML and CSS, javascript and jQuery are the only ones I'm experienced with.
Any and all help would be greatly appreciated.
I think your question is similar to
Convert String to XML Document in JavaScript
The answer is that you can wrap it in a jQuery object. Then use jQuery's normal DOM manipulation methods on it.
var myhtml = $($('#main-input').val());
myhtml.find('a').each(function () {
alert($(this).text());
});
if it's a top level element you need to use filter instead of find.
You can create a jQuery object from html strings outside of the DOM and maniuplate it just the same as if it was in the DOM.
Simple example:
var html='<div><p>ABC</p></div>';
alert( $(html).find('p').text() ); // alerts "ABC"
Or
var $div= $('<div>').append(html).find('p').after('<p>DEF</p>');
var newHtml= $div.html();
Will return
<div>
<p>ABC</p>
<p>DEF</p>
</div>
Conclusion, I would loop through a jQuery object created from your html and do what you need using jQuery methods

Add DOM elements to beginning of parent

I have the following javascript working to insert AJAX responses into a div with id results:
document.getElementById("results").innerHTML=xmlhttp.responseText;
However, this adds all new elements after those already present. I need for the new elements to be inserted before everything else.
I know this is probably very trivial but I can't seem to find anyway to do it myself.
Thanks for any help!
With modern js you can utilize the prepend method. Currently caniuse.com says only of IE, Edge, and OperaMini are not supported.
ParentNode.prepend(nodesToPrepend);
e.g.,
ParentNode.prepend(newDiv);
Also, it automatically converts text into a text node so you could do the following for your use case:
document.getElementById("results").prepend(xmlhttp.responseText);
You want either this
results.insertAdjacentHTML( 'beforebegin', xmlhttp.responseText );
or this
results.insertAdjacentHTML( 'afterbegin', xmlhttp.responseText );
(the variable results of course being a reference to the DOM element)
So, the first one will insert the new content before the element itself (as a previous sibling), and the second one will insert it inside the element before any other children).
I don't remember the exact syntax, but it something like:
var newDiv = document.createElement("div");
newDiv.innerHTML=xmlhttp.responseText;
document.getElementById("results").childNodes.addAt(0,newDiv);
if you can use jQuery, it's just simple as:
$("#results").prepend(xmlhttp.responseText);

Does jQuery strip some html elements from a string when using .html()?

I have a var that contains a full html page, including the head, html, body, etc. When I pass that string into the .html() function, jQuery strips out all those elements, such as body, html, head, etc, which I don't want.
My data var contains:
<html>
<head>
<title>Untitled Document</title>
</head>
<body>
</body>
</html>
Then my jQuery is:
// data is a full html document string
data = $('<div/>').html(data);
// jQuery stips my document string!
alert(data.find('head').html());
I am needing to manipulate a full html page string, so that I can return what is in the element. I would like to do this with jQuery, but it seems all of the methods, append(), prepend() and html() all try to convert the string to dom elements, which remove all the other parts of a full html page.
Is there another way that I could do this? I would be fine using another method. My final goal is to find certain elements inside my string, so I figured jQuery would be best, since I am so used to it. But, if it is going to trim and remove parts of my string, I am going to have to look for another method.
Ideas?
After a few quick tests it seems do me that this behavior isn't caused by jQuery but instead by the browser.
As you can easily verify yourself (DEMO http://jsbin.com/ocupa3)
var data = "<html><head><title>Untitled Document</title></head><body><p>test</p></body></html>";
data = $('<div/>').html(data);
alert(data.html());
yields different results in different browsers
Opera 10.10
<HEAD><TITLE>Untitled Document</TITLE></HEAD><P>test</P>
FF 3.6
<title>Untitled Document</title><p>test</p>
IE6
<P>test</P>
so this has nothing to do with jQuery, It's the browsers which strip some tags when you insert a whole html string inside a div. But you would need to step through the whole jQuery code for html() to be sure. And you would need to do that for all browsers as there are several different ways jQuery tries to do it's job.
For a solution I advise you to investigate using an iframe (possibly hidden) and to set that iframe content to the html-string you have. But be aware that fiddling with iframes and changing their content programmatically isn't an easy task either. There are also different browser related quirks and timing issues involved.
Here is a solution, which will include the body, head and other attributes:
mydoc = document.getElementById('NAME_OF_PREVIEW_FRAME').contentWindow.document; mydoc.write(HTML_CODE); mydoc.close();
Nope, the jQuery html function is just sending the string through to the element's innerHTML property, which is a function of the browser that tells it to parse the HTML into DOM elements and add them to the page.
Your browser doesn't work with a page as HTML data, it works with it as DOM and imports/exports HTML.
JavaScript has very good Regular Expression support. Depending on the complexity of your task, you may find this is the best way to process your data.
There is no need for the container div.
Have you tried this?:
var foo = $(data); // data is your full html document string
Then you can search inside of it like so:
$('.someClass', foo); // foo is the document you created earlier
Update:
As another answered mentioned, how this will act comes down to the browser.
I looked at the jQuery docs a bit and found this:
When the HTML is more complex than a
single tag without attributes, as it
is in the above example, the actual
creation of the elements is handled by
the browser's innerHTML mechanism.
Specifically, jQuery creates a new
<div> element and sets the innerHTML
property of the element to the HTML
snippet that was passed in.
So it seems that when you are using a whole html doc as a string, it's no different than setting the innerHTML property of a div you make using createElement.

Categories

Resources