This question already has answers here:
How do I get the entire page's HTML with jQuery?
(5 answers)
Closed 9 years ago.
This event is triggered when I make it:
$(document).on('dblclick', function() {
alert($(document).html());
});
But the exception is thrown in the console:
Timestamp: 20.7.2013 18:59:35
Error: TypeError: t is null
Source File: http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
Line: 5
Is this a jQuery related issue or I'm using a wrong approach to get entire HTML of a current HTML document?
If you want everything, including the DOCTYPE and understand that it is generated source and not original source, you can use XMLSerializer on document.
$(document).on('dblclick', function() {
alert(new XMLSerializer().serializeToString(document));
});
Try this code rather :
$(document).on('dblclick', function() {
alert($('html').html());
});
document is not an element, so it doesn't have a way to render itself as HTML. There's currently no compatible way to get the entire actual source, short of an Ajax request for the current URL -- and even that may not return the same thing.
$('html').html() will get you everything inside the <html> tag. In most cases, that's plenty. Tack on a <!DOCTYPE html>, and you have yourself a valid HTML document (assuming the original source represented one).
document.documentElement.outerHtml to get most of the document. That'll include the <html> tag, but not anything validly outside of it. Specifically, it won't get you the doctype, or any comments that appear before or after.
For modern browsers, a new XMLSerializer may get you the current contents of the document. It doesn't work in IE8, though.
And of course, all of these methods of telling the browser to HTMLify the document, return the contents as the browser understands them. The browser may mangle invalid HTML to get it to fit within the document. For example, stuff appearing after the <body> element may be moved to inside it.
Related
In tutorials I've learnt to use document.write. Now I understand that by many this is frowned upon. I've tried print(), but then it literally sends it to the printer.
So what are alternatives I should use, and why shouldn't I use document.write? Both w3schools and MDN use document.write.
The reason that your HTML is replaced is because of an evil JavaScript function: document.write().
It is most definitely "bad form." It only works with webpages if you use it on the page load; and if you use it during runtime, it will replace your entire document with the input. And if you're applying it as strict XHTML structure it's not even valid code.
the problem:
document.write writes to the document stream. Calling document.write on a closed (or loaded) document automatically calls document.open which will clear the document.
-- quote from the MDN
document.write() has two henchmen, document.open(), and document.close(). When the HTML document is loading, the document is "open". When the document has finished loading, the document has "closed". Using document.write() at this point will erase your entire (closed) HTML document and replace it with a new (open) document. This means your webpage has erased itself and started writing a new page - from scratch.
I believe document.write() causes the browser to have a performance decrease as well (correct me if I am wrong).
an example:
This example writes output to the HTML document after the page has loaded. Watch document.write()'s evil powers clear the entire document when you press the "exterminate" button:
I am an ordinary HTML page. I am innocent, and purely for informational purposes. Please do not <input type="button" onclick="document.write('This HTML page has been succesfully exterminated.')" value="exterminate"/>
me!
the alternatives:
.innerHTML This is a wonderful alternative, but this attribute has to be attached to the element where you want to put the text.
Example: document.getElementById('output1').innerHTML = 'Some text!';
.createTextNode() is the alternative recommended by the W3C.
Example: var para = document.createElement('p');
para.appendChild(document.createTextNode('Hello, '));
NOTE: This is known to have some performance decreases (slower than .innerHTML). I recommend using .innerHTML instead.
the example with the .innerHTML alternative:
I am an ordinary HTML page.
I am innocent, and purely for informational purposes.
Please do not
<input type="button" onclick="document.getElementById('output1').innerHTML = 'There was an error exterminating this page. Please replace <code>.innerHTML</code> with <code>document.write()</code> to complete extermination.';" value="exterminate"/>
me!
<p id="output1"></p>
Here is code that should replace document.write in-place:
document.write=function(s){
var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length-1];
lastScript.insertAdjacentHTML("beforebegin", s);
}
You can combine insertAdjacentHTML method and document.currentScript property.
The insertAdjacentHTML() method of the Element interface parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position:
'beforebegin': Before the element itself.
'afterbegin': Just inside the element, before its first child.
'beforeend': Just inside the element, after its last child.
'afterend': After the element itself.
The document.currentScript property returns the <script> element whose script is currently being processed. Best position will be beforebegin — new HTML will be inserted before <script> itself. To match document.write's native behavior, one would position the text afterend, but then the nodes from consecutive calls to the function aren't placed in the same order as you called them (like document.write does), but in reverse. The order in which your HTML appears is probably more important than where they're place relative to the <script> tag, hence the use of beforebegin.
document.currentScript.insertAdjacentHTML(
'beforebegin',
'This is a document.write alternative'
)
As a recommended alternative to document.write you could use DOM manipulation to directly query and add node elements to the DOM.
Just dropping a note here to say that, although using document.write is highly frowned upon due to performance concerns (synchronous DOM injection and evaluation), there is also no actual 1:1 alternative if you are using document.write to inject script tags on demand.
There are a lot of great ways to avoid having to do this (e.g. script loaders like RequireJS that manage your dependency chains) but they are more invasive and so are best used throughout the site/application.
I fail to see the problem with document.write. If you are using it before the onload event fires, as you presumably are, to build elements from structured data for instance, it is the appropriate tool to use. There is no performance advantage to using insertAdjacentHTML or explicitly adding nodes to the DOM after it has been built. I just tested it three different ways with an old script I once used to schedule incoming modem calls for a 24/7 service on a bank of 4 modems.
By the time it is finished this script creates over 3000 DOM nodes, mostly table cells. On a 7 year old PC running Firefox on Vista, this little exercise takes less than 2 seconds using document.write from a local 12kb source file and three 1px GIFs which are re-used about 2000 times. The page just pops into existence fully formed, ready to handle events.
Using insertAdjacentHTML is not a direct substitute as the browser closes tags which the script requires remain open, and takes twice as long to ultimately create a mangled page. Writing all the pieces to a string and then passing it to insertAdjacentHTML takes even longer, but at least you get the page as designed. Other options (like manually re-building the DOM one node at a time) are so ridiculous that I'm not even going there.
Sometimes document.write is the thing to use. The fact that it is one of the oldest methods in JavaScript is not a point against it, but a point in its favor - it is highly optimized code which does exactly what it was intended to do and has been doing since its inception.
It's nice to know that there are alternative post-load methods available, but it must be understood that these are intended for a different purpose entirely; namely modifying the DOM after it has been created and memory allocated to it. It is inherently more resource-intensive to use these methods if your script is intended to write the HTML from which the browser creates the DOM in the first place.
Just write it and let the browser and interpreter do the work. That's what they are there for.
PS: I just tested using an onload param in the body tag and even at this point the document is still open and document.write() functions as intended. Also, there is no perceivable performance difference between the various methods in the latest version of Firefox. Of course there is a ton of caching probably going on somewhere in the hardware/software stack, but that's the point really - let the machine do the work. It may make a difference on a cheap smartphone though. Cheers!
The question depends on what you are actually trying to do.
Usually, instead of doing document.write you can use someElement.innerHTML or better, document.createElement with an someElement.appendChild.
You can also consider using a library like jQuery and using the modification functions in there: http://api.jquery.com/category/manipulation/
This is probably the most correct, direct replacement: insertAdjacentHTML.
Try to use getElementById() or getElementsByName() to access a specific element and then to use innerHTML property:
<html>
<body>
<div id="myDiv1"></div>
<div id="myDiv2"></div>
</body>
<script type="text/javascript">
var myDiv1 = document.getElementById("myDiv1");
var myDiv2 = document.getElementById("myDiv2");
myDiv1.innerHTML = "<b>Content of 1st DIV</b>";
myDiv2.innerHTML = "<i>Content of second DIV element</i>";
</script>
</html>
Use
var documentwrite =(value, method="", display="")=>{
switch(display) {
case "block":
var x = document.createElement("p");
break;
case "inline":
var x = document.createElement("span");
break;
default:
var x = document.createElement("p");
}
var t = document.createTextNode(value);
x.appendChild(t);
if(method==""){
document.body.appendChild(x);
}
else{
document.querySelector(method).appendChild(x);
}
}
and call the function based on your requirement as below
documentwrite("My sample text"); //print value inside body
documentwrite("My sample text inside id", "#demoid", "block"); // print value inside id and display block
documentwrite("My sample text inside class", ".democlass","inline"); // print value inside class and and display inline
I'm not sure if this will work exactly, but I thought of
var docwrite = function(doc) {
document.write(doc);
};
This solved the problem with the error messages for me.
Inserting dynamic SVG content into the DOM does not work as expected, having the SVG element onload attribute (containing JavaScript) regarding: "setInterval()".
As noted in the search tags of this question; this is plain (valilla) JavaScript (not jQuery); here's a breakdown of the issue:
I have some SVG code (plain text) that gets inserted into a <div> as innerHTML
the SVG element has an onload attribute with some JavaScript inside it
the JavaScript contains setInterval(...) - (which does not work at all)
I grab the SVG element from the temporary div and return it as the result of a function.
this result is appended into some element in the live DOM (body)
the strange issue:
any other code inside that onload attribute works fine,
only setInterval & setTimeout is completely ignored
More info:
During runtime (start-up), the SVG code is grabbed from an existing embed element .getSVGDocument() (after it has loaded) and prepared as plain HTML which can just be used as a template to create many others from the same source-code. I'm not using cloneNode(true) -because: the interval is for animation (continuous slow & smooth rotation) - which could have a heavy impact on client-side resources, hence, I thought it best to grab the code and keep it as template - then remove the original from the DOM.
With all the above in mind, everything works fine:
The (new) SVG shows up on screen, all nice and dandy-like
When I console.log the (inline) SVG code that is used, all looks perfect
I get no errors, and there is no error handler that mutes errors (window.onerror == null)
The JavaScript (text) inside the SVG node's onload attribute works for things like: console.log(this) - (which produces the SVG element in the log) - however, as mentioned: setInterval() & setTimeout() is just simply ignored - without any error and no warning.
The following code is a very short example, and (regrettably) it works; however, in my production app it doesn't.
The code:
var html = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" onload="setInterval(function(){ console.log(\'testing\'); },500);">';
var temp = document.createElement('div'); temp.innerHTML = html;
var node = temp.getElementsByTagName('svg')[0];
document.body.appendChild(node);
If you test the above code in a new js file, it works; however, for the life of me I can't find the reason why it breaks in my app; as explained, it's quite simple really.
The question:
Does anyone know if there is some "gotcha" I'm not aware of regarding this? Maybe name-spacing?
If the source-code is required, I can load it up on JSFiddle, or CodePen -if required, but, it's a lot of code, and many files, which may not be necessary for publication.
I'm sure it's just something small; like, how timers register according to scope, and maybe how it's affected in .bind() ?
I'm really stuck with this, and I kinda need it working for a good impression for a job-interview; so if you know anything that could help, I would appreciate your input very much.
Thank you.
embedded content, onload attributes & the DOM
The following may help in related scenarios:
when targeting an asynchronous source, make sure the contentDocument or getSVGDocument() contains the resources you need to access. The window.onload, or the DOMContentLoaded event is relative to the current DOM, so it may help constructing your own listener->trigger for a cross-browser solution, because the contents you need may not be ready in a synchronous fashion.
the onload attribute/event is not triggered when inserting dynamic content that is not asynchronously loaded, but may fire under certain circumstances, so, again, a custom:
listen->trigger will solve that.
question specific
The question is directly related to the 2nd point above, and the answer is quite simple really:
in the "onload" attribute of said SVG, set a simple value as property of this like:
<svg onload="this.ready = true; setTinterval(...)"
in the constructor function, after the element was dynamically created, simply check if the svg-node's onload event was fired like so:
if (!svgNode.ready){ svgNode.onload(); }
If there is an error in your code, but no error is shown, make sure window.onerror is either null -or if it's a function, make sure it does NOT return true - else it may suppress errors and you'll have a hard time tracking down what's wrong.
Please feel free to either improve this answer, or comment and I'll improve it accordingly; however, better answers will be appreciated.
6 years later...
With vanilla JavaScript Web Components you can do:
<load-svg></load-svg>
<script>
customElements.define("load-svg", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<svg></svg>`;
setInterval(() => {
console.log("testing");
}, 500);
}
});
</script>
This question already has answers here:
How may I reference the script tag that loaded the currently-executing script?
(14 answers)
Closed 8 years ago.
I need to know if it is possible to obtain the current execution node?
Example:
..html
<script id="x">
console.log(document.currentNode.id); // << this must return "x"
</script>
..html
Thanks!
Modern browsers support a standard property on document object that points to the current script node:
https://developer.mozilla.org/en-US/docs/DOM/document.currentScript
<script id="x">
console.log(document.currentScript.id); // << this must return "x"
</script>
I don't know for 100% sure, but the collection of script tags returned by document.getElementsByTagName('script') should not include the script tags that are below the presently executing <script>. In other words, I think that this should work:
var arrScripts = document.getElementsByTagName('script');
var strScriptTagId = arrScripts[arrScripts.length - 1].id;
This will only work for scripts that are executing as the page loads. If this executes in a deferred script or as part of a page-load event, it will likely always report the last <script> in the completely rendered page. Note that the id tag is not a valid attribute for <script>, so your page won't likely validate. If you have any external scripts that write script tags (for example, third party ads), I think the code will be unpredictable. I played with this idea a couple years ago and the results were unsatisfactory.
Andrews Answer already has been a good idea but I experienced all the issues mentioned.
This is why I choosed a different approach which works well for IE,FF and Chrome.
Simply executing the script in an onload event of an image. Defining a transparent 1pixel gif inline and you will receive "this" when it fires.
This example is used to change DIV content dynamically while rendering. My target is to fill the div with a different innerHTML created by an browser based xsl rendering (not shown here).
For sure you even can load any image from the internet so it must not be inline. And the big benefit: the image and its event are replacing themself with the new content so even the image will disappear. The "div" even does not need any "id" assignment.
<div id="demo">
empty
<img onLoad="changeNodeOnTheFly(this,'hurra');void(0);" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="/>
</div>
The script:
<script>
function changeNodeOnTheFly(ele, text)
{
ele.parentNode.innerHTML=text;
}
</script>
BR
Heiko
Use document.write to find your position:
<script data-foo="bar">
var id = 'placeholder-' + Math.floor(Math.random() * 1e10)
document.write('<div id=' + id + '></div>')
var placeholder = document.getElementById(id)
var script = placeholder.previousSibling
placeholder.parentNode.removeChild(placeholder)
// "bar" is written to the document
document.write(script.getAttribute('data-foo'))
</script>
Why not use:
<script id="x">
console.log(document.getElementById("x").id);
</script>
This question already has answers here:
How may I reference the script tag that loaded the currently-executing script?
(14 answers)
Closed 8 years ago.
I need to know if it is possible to obtain the current execution node?
Example:
..html
<script id="x">
console.log(document.currentNode.id); // << this must return "x"
</script>
..html
Thanks!
Modern browsers support a standard property on document object that points to the current script node:
https://developer.mozilla.org/en-US/docs/DOM/document.currentScript
<script id="x">
console.log(document.currentScript.id); // << this must return "x"
</script>
I don't know for 100% sure, but the collection of script tags returned by document.getElementsByTagName('script') should not include the script tags that are below the presently executing <script>. In other words, I think that this should work:
var arrScripts = document.getElementsByTagName('script');
var strScriptTagId = arrScripts[arrScripts.length - 1].id;
This will only work for scripts that are executing as the page loads. If this executes in a deferred script or as part of a page-load event, it will likely always report the last <script> in the completely rendered page. Note that the id tag is not a valid attribute for <script>, so your page won't likely validate. If you have any external scripts that write script tags (for example, third party ads), I think the code will be unpredictable. I played with this idea a couple years ago and the results were unsatisfactory.
Andrews Answer already has been a good idea but I experienced all the issues mentioned.
This is why I choosed a different approach which works well for IE,FF and Chrome.
Simply executing the script in an onload event of an image. Defining a transparent 1pixel gif inline and you will receive "this" when it fires.
This example is used to change DIV content dynamically while rendering. My target is to fill the div with a different innerHTML created by an browser based xsl rendering (not shown here).
For sure you even can load any image from the internet so it must not be inline. And the big benefit: the image and its event are replacing themself with the new content so even the image will disappear. The "div" even does not need any "id" assignment.
<div id="demo">
empty
<img onLoad="changeNodeOnTheFly(this,'hurra');void(0);" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="/>
</div>
The script:
<script>
function changeNodeOnTheFly(ele, text)
{
ele.parentNode.innerHTML=text;
}
</script>
BR
Heiko
Use document.write to find your position:
<script data-foo="bar">
var id = 'placeholder-' + Math.floor(Math.random() * 1e10)
document.write('<div id=' + id + '></div>')
var placeholder = document.getElementById(id)
var script = placeholder.previousSibling
placeholder.parentNode.removeChild(placeholder)
// "bar" is written to the document
document.write(script.getAttribute('data-foo'))
</script>
Why not use:
<script id="x">
console.log(document.getElementById("x").id);
</script>
How exactly does it relate to jQuery? I know the library uses native javascript functions internally, but what exactly is it trying to do whenever such a problem appears?
It means you've tried to insert a DOM node into a place in the DOM tree where it cannot go. The most common place I see this is on Safari which doesn't allow the following:
document.appendChild(document.createElement('div'));
Generally, this is just a mistake where this was actually intended:
document.body.appendChild(document.createElement('div'));
Other causes seen in the wild (summarized from comments):
You are attempting to append a node to itself
You are attempting to append null to a node
You are attempting to append a node to a text node.
Your HTML is invalid (e.g. failing to close your target node)
The browser thinks the HTML you are attempting to append is XML (fix by adding <!doctype html> to your injected HTML, or specifying the content type when fetching via XHR)
If you are getting this error due to a jquery ajax call $.ajax
Then you may need to specify what the dataType is coming back from the server. I have fixed the response a lot using this simple property.
$.ajax({
url: "URL_HERE",
dataType: "html",
success: function(response) {
$('#ELEMENT').html(response);
}
});
Specifically with jQuery you can run into this issue if forget the carets around the html tag when creating elements:
$("#target").append($("div").text("Test"));
Will raise this error because what you meant was
$("#target").append($("<div>").text("Test"));
This error can occur when you try to insert a node into the DOM which is invalid HTML, which can be something as subtle as an incorrect attribute, for example:
// <input> can have a 'type' attribute
var $input = $('<input/>').attr('type', 'text');
$holder.append($input); // OK
// <div> CANNOT have a 'type' attribute
var $div = $('<div></div>').attr('type', 'text');
$holder.append($div); // Error: HIERARCHY_REQUEST_ERR: DOM Exception 3
#Kelly Norton is right in his answer that The browser thinks the HTML you are attempting to append is XML and suggests specifying the content type when fetching via XHR.
It's true however you sometimes use third party libraries that you are not going to modify. It's JQuery UI in my case. Then you should provide the right Content-Type in the response instead of overriding the response type on JavaScript side. Set your Content-Type to text/html and you are fine.
In my case, it was as easy as renaming the file.xhtml to file.html - application server had some extension to MIME types mappings out of the box. When content is dynamic, you can set the content type of response somehow (e.g. res.setContentType("text/html") in Servlet API).
You can see these questions
Getting HIERARCHY_REQUEST_ERR when using Javascript to recursively generate a nested list
or
jQuery UI Dialog with ASP.NET button postback
The conclusion is
when you try to use function append, you should use new variable, like this example
jQuery(function() {
var dlg = jQuery("#dialog").dialog({
draggable: true,
resizable: true,
show: 'Transfer',
hide: 'Transfer',
width: 320,
autoOpen: false,
minHeight: 10,
minwidth: 10
});
dlg.parent().appendTo(jQuery("form:first"));
});
In the example above, uses the var "dlg" to run the function appendTo.
Then error “HIERARCHY_REQUEST_ERR" will not come out again.
I encountered this error when using the Google Chrome extension Sidewiki. Disabling it resolved the issue for me.
I'm going to add one more specific answer here because it was a 2 hour search for the answer...
I was trying to inject a tag into a document. The html was like this:
<map id='imageMap' name='imageMap'>
<area shape='circle' coords='55,28,5' href='#' title='1687.01 - 0 percentile' />
</map>
If you notice, the tag is closed in the preceding example (<area/>). This was not accepted in Chrome browsers. w3schools seems to think it should be closed, and I could not find the official spec on this tag, but it sure doesn't work in Chrome. Firefox will not accept it with <area/> or <area></area> or <area>. Chrome must have <area>. IE accepts anything.
Anyway, this error can be because your HTML is not correct.
I know this thread is old, but I've encountered another cause of the problem which others might find helpful. I was getting the error with Google Analytics trying to append itself to an HTML comment. The offending code:
document.documentElement.firstChild.appendChild(ga);
This was causing the error because my first element was an HTML comment (namely a Dreamweaver template code).
<!-- #BeginTemplate "/Templates/default.dwt.php" -->
I modified the offending code to something admittedly not bulletproof, but better:
document.documentElement.firstChild.nodeType===1 ? document.documentElement.firstChild.appendChild(ga) : document.documentElement.lastChild.appendChild(ga);
If you run into this problem while trying to append a node into another window in Internet Explorer, try using the HTML inside the node instead of the node itself.
myElement.appendChild(myNode.html());
IE doesn't support appending nodes to another window.
This ERROR happened to me in IE9 when I tried to appendChild an dynamically to a which already existed in a window A. Window A would create a child window B. In window B after some user action a function would run and do an appendChild on the form element in window A using window.opener.document.getElementById('formElement').appendChild(input);
This would throw an error. Same with creating the input element using document.createElement('input'); in the child window, passing it as a parameter to the window.opener window A, and there do the append. Only if I created the input element in the same window where I was going to append it, it would succeed without errors.
Thus my conclusion (please verify): no element can be dynamically created (using document.createElement) in one window, and then appended (using .appendChild) to an element in another window (without taking maybe a particular extra step I missed to ensure it is not considered XML or something). This fails in IE9 and throws the error, in FF this works fine though.
PS. I don't use jQuery.
Another reason this can come up is that you are appending before the element is ready e.g.
<body>
<script>
document.body.appendChild(foo);
</script>
</body>
</html>
In this case, you'll need to move the script after the . Not entirely sure if that's kosher, but moving the script after the body doesn't seem to help :/
Instead of moving the script, you can also do the appending in an event handler.
I got that error because I forgot to clone my element.
// creates an error
clone = $("#thing");
clone.appendTo("#somediv");
// does not
clone = $("#thing").clone();
clone.appendTo("#somediv");
Just for reference.
IE will block appending any element created in a different window context from the window context that the element is being appending to.
e.g
var childWindow = window.open('somepage.html');
//will throw the exception in IE
childWindow.document.body.appendChild(document.createElement('div'));
//will not throw exception in IE
childWindow.document.body.appendChild(childWindow.document.createElement('div'));
I haven't figured out how to create a dom element with jQuery using a different window context yet.
I get this error in IE9 if I had disabled script debugging (Internet Explorer) option. If I enable script debugging I don't see the error and the page works fine. This seems odd what is the DOM exception got to do with debugging either enabled or disabled.