When I use the console and I try to create an element with tag frameset, I get no result:
$('<div id="content" data-something="hello" />')
=> [<div id="content" data-something="hello"></div>]
$('<frameset frameborder="0" framespacing="0" marginwidth="0" marginheight="0" framespacing="0" border="0"></frameset>')
=> []
This behaviour persists across multiple JQuery versions (like 1.10.2, 2.0.0 and 1.2.6 etc.)
How can I read the 'frameborder' (for example) attribute from this frameset without having to build a parser by mself?
P.S. (If you wonder why I use frames) This line (or a line like this) is a response from an external (more or less) API that I cannot change. I would just like to read the information and go on.
The frameset tag is to be used as the body of framed documents in place of the body tag, and in conjuction with the frameset document type declaration. It is considered obsolete since HTML5.
To solve your issue, your best bet is to parse the portions you require from the string on your own or use an HTML parsing library such as htmlparser.js
The attributes on your frameset element are not valid HTML, so jQuery does not create it. It will work if you take them out. You can then add the attributes one at a time using .attr.
var x = $('<frameset></frameset>');
x.attr('frameborder', '0');
x.attr('framespacing', '0');
x.attr('border', '0');
But the added code and resource cost of creating an element is not necessary just to find an attribute value in the string. You can find the substring you are looking for with the match method like this:
var frameborder = '<frameset frameborder="0" border="0"></frameset>'.match(/frameborder="(.+?)"/)[1]
Just replace 'frameborder' in the regex with the name of another attribute to get its value. Simple.
I used a workaround:
var sourceWithFrames = ...
sourceWithFrames = sourceWithFrames.replace(/<frame/g, '<xyzFrame')
// e.g.
var frameborder = $(sourceWithFrames).find('xyzFrameset').attr('frameborder')
// and so on
This is, in my opinion, the best way to approach this (and probably the only one...)
Related
I'm making an ajax call to fetch content and append this content like this:
$(function(){
var site = $('input').val();
$.get('file.php', { site:site }, function(data){
mas = $(data).find('a');
mas.map(function(elem, index) {
divs = $(this).html();
$('#result').append('' + divs + '');
})
}, 'html');
});
The problem is that when I change a in body I get nothing (no error, just no html). Im assuming body is a tag just like 'a' is? What am I doing wrong?
So this works for me:
mas = $(data).find('a');
But this doesn't:
mas = $(data).find('body');
I ended up with this simple solution:
var body = data.substring(data.indexOf("<body>")+6,data.indexOf("</body>"));
$('body').html(body);
Works also with head or any other tag.
(A solution with xml parsing would be nicer but with an invalid XML response you have to do some "string parsing".)
Parsing the returned HTML through a jQuery object (i.e $(data)) in order to get the body tag is doomed to fail, I'm afraid.
The reason is that the returned data is a string (try console.log(typeof(data))). Now, according to the jQuery documentation, when creating a jQuery object from a string containing complex HTML markup, tags such as body are likely to get stripped. This happens since in order to create the object, the HTML markup is actually inserted into the DOM which cannot allow such additional tags.
Relevant quote from the documentation:
If a string is passed as the parameter to $(), jQuery examines the string to see if it looks like HTML.
[...]
If 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. In most cases, jQuery creates a new element and sets the innerHTML property of the element to the HTML snippet that was passed in. When the parameter has a single tag (with optional closing tag or quick-closing) — $( "< img / >" ) or $( "< img >" ), $( "< a >< /a >" ) or $( "< a >" ) — jQuery creates the element using the native JavaScript createElement() function.
When passing in complex HTML, some browsers may not generate a DOM
that exactly replicates the HTML source provided. As mentioned, jQuery
uses the browser"s .innerHTML property to parse the passed HTML and
insert it into the current document. During this process, some
browsers filter out certain elements such as < html >, < title >, or
< head > elements. As a result, the elements inserted may not be
representative of the original string passed.
I experimented a little, and have identified the cause to a point, so pending a real answer which I would be interested in, here is a hack to help understand the issue
$.get('/',function(d){
// replace the `HTML` tags with `NOTHTML` tags
// and the `BODY` tags with `NOTBODY` tags
d = d.replace(/(<\/?)html( .+?)?>/gi,'$1NOTHTML$2>',d)
d = d.replace(/(<\/?)body( .+?)?>/gi,'$1NOTBODY$2>',d)
// select the `notbody` tag and log for testing
console.log($(d).find('notbody').html())
})
Edit: further experimentation
It seems it is possible if you load the content into an iframe, then you can access the frame content through some dom object hierarchy...
// get a page using AJAX
$.get('/',function(d){
// create a temporary `iframe`, make it hidden, and attach to the DOM
var frame = $('<iframe id="frame" src="/" style="display: none;"></iframe>').appendTo('body')
// check that the frame has loaded content
$(frame).load(function(){
// grab the HTML from the body, using the raw DOM node (frame[0])
// and more specifically, it's `contentDocument` property
var html = $('body',frame[0].contentDocument).html()
// check the HTML
console.log(html)
// remove the temporary iframe
$("#frame").remove()
})
})
Edit: more research
It seems that contentDocument is the standards compliant way to get hold of the window.document element of an iFrame, but of course IE don't really care for standards, so this is how to get a reference to the iFrame's window.document.body object in a cross platform way...
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
var iframeBody = iframeDoc.body;
// or for extra caution, to support even more obsolete browsers
// var iframeBody = iframeDoc.getElementsByTagName("body")[0]
See: contentDocument for an iframe
I FIGURED OUT SOMETHING WONDERFUL (I think!)
Got your html as a string?
var results = //probably an ajax response
Here's a jquery object that will work exactly like the elements currently attached to the DOM:
var superConvenient = $($.parseXML(response)).children('html');
Nothing will be stripped from superConvenient! You can do stuff like superConvenient.find('body') or even
superConvenient.find('head > script');
superConvenient works exactly like the jquery elements everyone is used to!!!!
NOTE
In this case the string results needs to be valid XML because it is fed to JQuery's parseXML method. A common feature of an HTML response may be a <!DOCTYPE> tag, which would invalidate the document in this sense. <!DOCTYPE> tags may need to be stripped before using this approach! Also watch out for features such as <!--[if IE 8]>...<![endif]-->, tags without closing tags, e.g.:
<ul>
<li>content...
<li>content...
<li>content...
</ul>
... and any other features of HTML that will be interpreted leniently by browsers, but will crash the XML parser.
Regex solution that worked for me:
var head = res.match(/<head.*?>.*?<\/head.*?>/s);
var body = res.match(/<body.*?>.*?<\/body.*?>/s);
Detailed explanation: https://regex101.com/r/kFkNeI/1
I'm currently trying to customize OpenCms (java-based open source CMS) a bit, which is using the FCKEditor embedded, which is what I'm trying access using js / jQuery.
I try to fetch the html content of the iframe, however, always getting null as a return.
This is how I try to fetch the html content from the iframe:
var editFrame = document.getElementById('ta_OpenCmsHtml.LargeNews_1_.Teaser_1_.0___Frame');
alert( $(editFrame).attr('id') ); // returns the correct id
alert( $(editFrame).contents().html() ); // returns null (!!)
Looking at the screenshot, the what I want to access is the 'LargeNews1/Teaser' html section, which currently holds the values "Newsline en...".
Below you can also see the html structure in Firebug.
However, $(editFrame).contents().html() returns null and I can't figure out why, whereas $(editFrame).attr('id') returns the correct id.
The iframe content / FCKEditor is on the same site/domain, no cross-site issues.
HTML code of iframe is at http://pastebin.com/hPuM7VUz
Updated:
Here's a solution that works:
var editArea = document.getElementById('ta_OpenCmsHtml.LargeNews_1_.Teaser_1_.0___Frame').contentWindow.document.getElementById('xEditingArea');
$(editArea).find('iframe:first').contents().find('html:first').find('body:first').html('some <b>new</b><br/> value');
.contents().html() doesn't work to get the HTML code of an IFRAME. You can do the following to get it:
$(editFrame).contents().find("html").html();
That should return all the HTML in the IFRAME for you. Or you can use "body" or "head" instead of "html" to get those sections too.
you can get the content as
$('#iframeID').contents().find('#someID').html();
but frame should be in the same domain refer http://simple.procoding.net/2008/03/21/how-to-access-iframe-in-jquery/
I suggest replacing the first line with:
var editFrame = $('#ta_OpenCmsHtml.LargeNews_1_.Teaser_1_.0___Frame');
...and the 2nd alert expression with:
editFrame.html()
If, on the other hand, you prefer to accomplish the same w/o jquery (much cooler, IMHO) could use only JavaScript:
var editFrame = document.getElementById('ta_OpenCmsHtml.LargeNews_1_.Teaser_1_.0___Frame');
alert(editFrame.innerHTML);
After trying a number of jQuery solutions that recommended using the option below, I discovered I was unable to get the actual <html> content including the parent tags.
$("#iframeId").contents().find("html").html()
This worked much better for me and I was able to fetch the entire <html>...</html> iframe content as a string.
document.getElementById('iframeId').contentWindow.document.documentElement.outerHTML
I think the FCKEditor has its own API see http://cksource.com/forums/viewtopic.php?f=6&t=8368
Looks like jQuery doesn't provide a method to fetch the entire HTML of an iFrame, however since it provides access to the native DOM element, a hybrid approach is possible:
$("iframe")[0].contentWindow.document.documentElement.outerHTML;
This will return iFrame's HTML including <THTML>, <HEAD> and <BODY>.
Your iframe:
<iframe style="width: 100%; height: 100%;" frameborder="0" aria-describedby="cke_88" title="Rich text editor, content" src="" tabindex="-1" allowtransparency="true"/>
We can get the data from this iframe as:
var content=$("iframe").contents().find('body').html();
alert(content);
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.
I need to give the user a snippet of js code that will insert some HTML code into the page.
I'm wondering what the best method to do so is. Should I use document.write, should I just create all the HTML elements via DOM programmatically?
Is it possible to use a js library? I can see conflicts occurring if the webpage the code is embedded in already contains the library.
Using a library is probably too heavyweight, inserting DOM elements is very verbose and document.write may not work if the target site uses the application/xhtml+xml content type. I think your best bet is to construct one element using document.createElement and then setting innerHTML on that.
A suggestion:
Insert this DIV wherever you want the output to appear:
<div id="uniqueTargetID" style="display: none;"></div>
Then at bottom of page have this:
<script src="snippet.js"></script>
This file (remotely hosted or otherwise) contains could output simple text this way:
var html = [];
html.push('<h1>This is a title</h1>');
html.push('<p>So then she said, thats not a monkey, its a truck!</p>');
html.push('<p>You shoulda seen his face...</p>');
var target = document.getElementById('uniqueTargetID');
target.innerHTML = html.join('');
target.style.display = 'block';
I would avoid using document.write() if you can help it.
Javascript::
//to avoid global bashing
(function(){
var target = document.getElementById('ScriptName'),
parent = target.parentElement,
oput = document.createElement('div');
oput.innerHTML = "<p>Some Content</p>";
parent.insertBefore(oput, target);
}());
HTML to give to client/people::
<script type="text/javascript" id="ScriptName" src="/path/to/ScriptName.js"><script>
ScriptName should be something unique to your script.
If its simple insertion you can use pure js, otherwise if you want to provide some complex functionality you can use library. The best choice in this case will be the lib that does not extend root objects (Array, Function, String) to prevent conflicts (jQuery in noConflict mode, YUI, etc.).
Anyway it will be better to avoid using document.write u'd better use setting of innerHTML of existing element or create new one.
Prototype's Template class allows you to easily substitute values into a string template. Instead of declaring the Template source-string in my code, I want to extract the source-string from the DOM.
For example, in my markup I have an element:
<div id="template1">
<img src="#{src}" title="#{title}" />
</div>
I want to create the template with the inner contents of the div element, so I've tried something like this:
var template = new Template($('template1').innerHTML);
The issue is that Internet Explorer's representation of the innerHTML omits the quotes around the attribute value when the value has no spaces. I've also attempted to use Element#inspect, but in Internet Explorer I get back a non-recursive representation of the element / sub-tree.
Is there another way to get a Template-friendly representation of the sub-tree's contents?
Looks like you can embed the template source inside a textarea tag instead of a div and retrieve it using Element#value.
Certainly makes the markup a little weird, but it still seems reasonably-friendly to designers.
Additionally, as Jason pointed out in a comment to the original question, including the img tag in the textarea prevents a spurious request for an invalid image.
Resig to the rescue:
You can also inline script:
<script type="text/html" id="user_tmpl">
<% for ( var i = 0; i < users.length; i++ ) { %>
<li><%=users[i].name%></li>
<% } %>
</script>
Quick tip: Embedding scripts in your
page that have a unknown content-type
(such is the case here - the browser
doesn't know how to execute a
text/html script) are simply ignored
by the browser - and by search engines
and screenreaders. It's a perfect
cloaking device for sneaking templates
into your page. I like to use this
technique for quick-and-dirty cases
where I just need a little template or
two on the page and want something
light and fast.
and you would use it from script like
so:
var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);