Parsing Ajax loaded HTML content in IE - javascript

I have seen similar questions around, but none of them seem to have answers that help my case...
Basically, I want to load some HTML in using $.ajax() (Which is on a different domain), and have it parsed into it's own DOM, so I can apply attributes and manipulate HTML in my actual window DOM.
$.ajax({
type: 'GET',
url: 'http://example.com/index.html',
dataType: 'html',
crossDomain: true,
cache: false,
success: function(data)
{
var src = $('body img', data).first().attr("src");
//also tried: var src = $('body', $(data)).first().attr("src");
$('#someDiv img').attr("src", src);
}
});
Where an example HTML file is:
<html>
<body>
<img src="someurl"></img>
</body>
</html>
It works in Firefox, but not IE, no matter what I try, whenever I try to parse and read, it returns null.
Any suggestions?
EDIT:
It appears there was some ambiguity with my question. The issue is the parsing, not the AJAX. The AJAX returns the html string correctly, but jQuery fails to parse it.
EDIT 2:
I found a 'solution', but it isn't nearly as nice as I wanted it to be, it chopping and sorting through the HTML string, and extracting data, rather than applying it to a DOM. Seems to run efficiently, as I can predict the order of data.
Boiled down, it is something like this:
var imgsrcs = new Array(5);
var searchItem = '<img src="';
for (var a=0; a<5; a++) {
var startLoc = data.search(searchItem) + searchItem.length;
for (var i=0; i<data.length; i++) {
if (data.charAt(startLoc + i) == '"')
break;
imgsrcs[a] += data.charAt(startLoc + i);
}
data = data.substring(startLoc + i, data.length);
}
$('.image').each(function(i) {
$(this).attr("src", imgsrcs[i]);
});
Fairly ugly, but I solved my problem, so I thought I may as well post it.

This is a Same Origin Policy problem.
The crossDomain flag in jquery's ajax function doesn't automatically make cross domain requests work in all browsers (not all browsers support CORS). Since you're requesting this from a different domain, a normal request won't actually be able to read the data (or even make the request).
Normally, for json data, you can do JSONP, which is what the crossDomain often flag enables. However, JSON is unique because it can be natively read in javascript. Since HTML cannot be read, you'd need to wrap it in parseable javascript to employ a trick like JSONP.
Rather than do that on your own, though, I'd highly suggest that you look into the easyXDM library in order to do cross domain messages like this. You'd essentially open up a hidden iframe on the other domain, and pass messages back and forth between the parent and the hidden frame. And, since the hidden frame is on the same domain as the html, it will have no problem ajaxing for it.
http://easyxdm.net/wp/

Related

Using jQuery on ajax response triggers additional network requests

I am writing a small script that takes a bunch of links from a page, fetches them and scours the results for some data.
E.g. like this:
let listLinks = $('.item a');
listLinks.each(function() {
let url = this.href;
fetch(url, {
credentials: 'include'
})
.then(response => response.text())
.then(function(html) {
let name = $('#title h1', html);
})
});
My problem is the fact that once we reach selector on the response the network tab in my browser's dev-tools lights up with requests for a ton of resources, as if something (jquery?) is just loading the entire page!
What the hell is going on here?
I don't want to load the entire page(resources and all), I just want to take a bunch of text from the html response!
Edit: After some more scrutiny, I discovered it only makes network requests for any images on the ajaxed page, but not scripts or stylesheets.
It does not make these requests if I try to process the html in another way - say, call .indexOf() on it. Only if I decide to traverse it via jquery.
Edit2: Poking around in dev tools, the network tab has an "initiator" column. It says this is the initiator for the requests: github code. I don't know what to make of that however...
P.S. Inb4 "just regex it".
I've discovered the cause:
My code above(relevant line):
$('#title h1', html)
is equivalent to
$(html).find('#title h1')
And $(html) essentially creates DOM elements. Actual, literal DOM objects.
When you create an <img> element(which the HTML I parse contains), the browser automatically issues a network request.
Relevant StackOverflow question:
Set img src without issuing a request
With the code in the question the created DOM elements are still associated with the current document(as noted here), therefore the browser automatically makes a request for new <img>s it doesn't have yet.
The correct solution is to create a separate document, e.g.
let parser = new DOMParser();
let doc = parser.parseFromString(html, "text/html");
let name = $('#title h1', doc);
No network requests go out in this case.
JSFiddle
The problem is that you are using fetch. Use jQuery.AJAX
$.ajax({
url: 'URL',
type: 'GET',
dataType: 'HTML',
success: function(responseHTML) {
console.log(responseHTML);
}
});

How to send user-created HTML to W3C validator for automatic checking—without cross-domain errors?

I am writing an application for users, in which they input valid HTML into a text field.
I have a button in jQuery which tries to load the text field area into the W3C validator:
$('#inspecthtml').on('click', function() {
var storyhtml = $('#story').text();
validatorurl= "http://validator.w3.org/#validate_by_input";
var newWin = open(validatorurl,'Validator','height=600,width=600');
newWin.onload = function() {
newWin.document.getElementById("fragment").value=storyhtml;
}
});
I get an error message in the console (using Chrome):
Unsafe JavaScript attempt to access frame with URL
http://api.flattr.com/button/view/?url=http%3A%2F%2Fvalidator.w3.org%2F&title=View%20W3C-Validator%20on%20flattr.com&
from frame with URL http://validator.w3.org/#validate_by_input. The
frame being accessed set 'document.domain' to 'flattr.com', but the
frame requesting access did not. Both must set 'document.domain' to
the same value to allow access.
I attribute this to the cross domain security (see Unsafe JavaScript attempt to access frame with URL)
My question: Is there a way to send the data to the validator, so my users can check their own mark-up?
I think the code snippet below will you can get the same effect and user experience you’re after.
It’s written using jQuery’s $.ajax(…) with some DOMParser and document.write(…) to put the styled results and UI of the W3C HTML Checker into a new window the way it seems you want.
var validator_baseurl= "https://validator.w3.org/nu/";
var validator_requesturl = validator_baseurl
+ "?showsource=yes&showoutline=yes";
$.ajax({
url: validator_requesturl,
type: "POST",
crossDomain: true,
data: storyhtml,
contentType: "text/html;charset=utf-8",
dataType: "html",
success: function (response) {
var results = (new DOMParser()).parseFromString(response, "text/html");
results.querySelector("link[rel=stylesheet]").href
= validator_baseurl + "style.css";
results.querySelector("script").src
= validator_baseurl + "script.js";
results.querySelector("form").action
= validator_requesturl;
var newWin = window.open("about:blank",
"Checker results", "height=825,width=700");
newWin.document.open();
newWin.document.write(results.documentElement.outerHTML);
newWin.document.close();
newWin.location.hash = "#textarea";
setTimeout(function() {
newWin.document.querySelector("textarea").rows = "5";
}, 1000)
}
});
Explanation
causes a POST request to be sent to the W3C HTML Checker
makes the storyhtml text the POST body
makes text/html;charset=utf-8 the POST body’s media type (what the checker expects)
causes the checker to actually check the storyhtml contents automatically
shows the checker results in a new window right when it’s first opened, in one step (so your users don’t need to do a second step to manually submit it for checking themselves)
replaces relative URLs for the checker’s frontend CSS+JS with absolute URLs (otherwise in this “standalone window” context, the CSS wouldn’t get applied, and the script wouldn’t run)
newWin.location.hash = "#textarea" is needed to make the checker show the textarea
Notes
intentionally uses the current W3C HTML Checker (not the legacy W3C markup validator)
intentionally sends the content to be checked as a POST body, not multipart/form-data); the checker supports multipart/form-data but making it a POST body is easier and better
the setTimeout textarea bit isn’t required; I just put it to make the results visible without scrolling (bottom part of new window below textarea); you can of course remove it if you want
sets the new window’s height and width a bit larger than the 600x600 in the question’s original code; again, I just did that to make things easier to see; change them however you want
uses standard DOM ops that may have better jQuery methods/idioms (I don’t normally use jQuery, so I can imagine there are ways to streamline the code in it further around JQuery)
could of course also be done without using jQuery at all—using standard Fetch or XHR instead (and I’d be happy to also add examples here that use Fetch and XHR if desired)
tested & works as expected in Edge, Firefox, Chrome & Safari; but as with any code that uses document.open, Safari users need to unset Preferences > Security > Block pop-up windows

JavaScript: How to open a returned file via AJAX

This is similar to: How to open a file using JavaScript?
Goal: to retrieve/open a file on an image's double click
function getFile(filename){
// setting mime this way is for example only
var mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
jQuery.ajax({ url : 'get_file.pl',
data : {filename:filename},
success : function(data){
var win = window.open('','title');
win.document.open(mime);
win.document.write(data);
win.document.close();
}
});
}
jQuery('#imgID').dblclick(function(){
getFile('someFile.docx');
});
I'm doing this off the top of my head, but I think the above would work for text files, but not binary. Is there a plugin that does this properly? The ideal would be to open the file in the browser (or application), rather than download, but I doubt that is a dream. If the file must be downloaded with the save/open dialog, that's fine.
Edit:
One piece of information that I forgot to mention is that I'd like this to be a POST request. This is partly why I was looking at AJAX to begin with. I've seen workarounds that have created forms/iframes to do something similar, but I was looking for a better handler of the returned info.
Seems to me there's no reason to do this via AJAX. Just open the new window to get_file.pl?filename=... and let the browser handle it. If the user has a plugin capable of handling the Content-Type sent by get_file.pl, the file will display; otherwise, it should download like any other file.
function getFile(filename) {
window.open('get_file.pl?filename=' + filename,'title');
}
jQuery('#imgID').dblclick(function() {
getFile('someFile.docx');
});
Edit: If you want to POST to your script, you can do it with some <form> hackery:
function getFile(filename) {
var win = 'w' + Math.floor(Math.random() * 10000000000000);
window.open('', win,'width=250,height=100');
var f = $('<form></form>')
.attr({target: win, method:'post', action: 'get_file.pl'})
.appendTo(document.body);
var i = $('<input>')
.attr({type:'hidden',name:'filename',value:filename})
.appendTo(f);
f[0].submit();
f.remove();
}
Of course, this is somewhat silly since it is impossible to hide your data from "prying eyes" with developer tools. If your filename really is sensitive, issue access tokens to the client, and look up the data in your sever script.

jquery load() equivalent for offline use

I am looking for an equivalent to jquery's load() method that will work offline. I know from jquery's documentation that it only works on a server. I have some files from which I need to call the html found inside a particular <div> in those files. I simply want to take the entire site and put it on a computer without an internet connection, and have that portion of the site (the load() portion) function just as if it was connected to the internet. Thanks.
Edit: BTW, it doesn't have to be js; it can be any language that will work.
Edit2:
My sample code (just in case there are syntax errors I am missing; this is for the files in the same directory):
function clickMe() {
var book = document.getElementById("book").value;
var chapter = document.getElementById("chapter").value;
var myFile = "'" + book + chapter + ".html'";
$('#text').load(myFile + '#source')
}
You can't achieve load() over the file protocol, no other ajax request is going to work for html files. I have tried even with the crossDomain and isLocale option on without anything success, even if precising the protocol.
The problem is that even if jQuery is trying the browser will stop the request for security issues (well most browsers as the snippet below works in FF) as it allows you to load locale file so you could get access to a lot of things.
The one thing you could load locally is javascript files, but that probably means changing a lot of the application/website architecture.
Only works in FF
$.ajax({
url: 'test.html',
type: 'GET',
dataType: 'text',
isLocale: true,
success: function(data) {
document.body.innerHTML = data;
}
});
What FF does well is that it detect that the file requesting local files is on the file protocol too when other don't. I am not sure if it has restriction over the type of files you can request.
You can still use the JQuery load function in this context:
You would could add an OfflineContent div on your page:
<div id="OfflineContent">
</div>
And then click a button which calls:
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
Button code:
$("#btnLoadContent").click(function() {
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
});
In the OfflinePage.html you could have to have another section called contentToLoad which would display on the initial page.

Get contents from <link> (not <a>) tag

Hi I'm trying to get contents of the link tag. So with:
<link rel="stylesheet" href="some.css">
I want the contents of the file some.css in a string.
Tried:
document.getElementsByTagName('link')[0].firstChild.nodeValue; // fails
document.getElementsByTagName('link')[0].hasChildNodes(); // false
Any ideas? I don't want to use the styleSheet method (which only works in FF anyway) because it will strip out stuff like -moz-border-radius and such.
Thanks.
I think Daniel A. White is correct. Your best bet is to get the href of the stylesheet, then load the content via Ajax and parse it.
What are you trying to do exactly?
You can't get the contents of a file with only javascript. You'll need an ajax request to the server which opens the file and returns its contents.
To do this, you need to access the file via an ajax request.
So, with jQuery, something like this
$.ajax({
url: "some.css",
success: function(){
//do something
}
});
More details here: http://api.jquery.com/jQuery.ajax/
Note: this only works if the file making the request is on the same server as the file requested.
CSS rules offer a special API, but nothing like innerHTML.
This is as close as it gets:
var result = '';
var st = document.styleSheets[0].cssRules;
for (var i = 0; i < st.length; i++) {
result += st[i].cssText;
}
console.log(result);
However, this will not respect whitespace, comments, erroneous rules, ...
And as usual, this is subject to Same Origin Policy.

Categories