I'm trying to get a consistent, cross browser outerHtml for a jQuery element, particularly $("html").outerHtml(). For example if the page source is this:
<html><script src="blah.js"></script><div class="foo" id='bar'></p></div></html>
I want to be able to use $("html").outerHtml(); to get the HTML string including the element itself, which would be:
<html><script src="blah.js"></script><div class="foo" id="bar"><p></p></div></html>
I've been using Brandon Aaron's outerHtml plugin which looks something like this:
return $('<div>').append($("html").first().clone()).html();
However this seems to actually try to reload any externally referenced files in the document (scripts, stylesheets), which seems pretty excessive just to get the HTML source of a wrapper element. Can Javascript even put an HTML element inside a DIV element?
Locally, I get this error. Something to do with AJAX rules?
XMLHttpRequest cannot load file:///C:/demo.js?_=1311466511031. Origin null is not allowed by Access-Control-Allow-Origin.
Is there a better way to get outerHtml? I'd really like to avoid any network calls when doing this.
Wrote my own solution, which simply renders the wrapping element:
(function($){
$.fn.outerHtml = function() {
if (this.length == 0) return false;
var elem = this[0], name = elem.tagName.toLowerCase();
if (elem.outerHTML) return elem.outerHTML;
var attrs = $.map(elem.attributes, function(i) { return i.name+'="'+i.value+'"'; });
return "<"+name+(attrs.length > 0 ? " "+attrs.join(" ") : "")+">"+elem.innerHTML+"</"+name+">";
};
})(jQuery);
https://gist.github.com/1102076
jQuery outerHTML plugin from https://github.com/darlesson/jquery-outerhtml uses browser's native outerHTML when existent, as second option clone the nodes in a temporary document fragment and return its innerHTML or uses jQuery clone solution similar to Brandon Aaron's plugin.
This plugin might help you avoid the load of the reference files. In my tests in Chrome, Firefox and Internet Explorer the issue did not happen calling the code below in a page with external JavaScript files.
var outerHTML = $(document.documentElement).outerHTML();
Related
I'm working on a chrome extension that uses jquery to parse the source of a page for specific things. In example I'm looking through Wikipedia to get the categories.
I get the source of the page via
chrome.tabs.executeScript(tabId, {
code: "chrome.extension.sendMessage({action: 'getContentText', source: document.body.innerHTML, location: window.location});"
}, function() {
if (chrome.extension.lastError)
console.log(chrome.extension.lastError.message);
});
I am then listening for this message (successfully) and then use jquery to parse the source key of the object, like so
if (request.action == "getContentText")
{
//console.log(request.source);
$('#mw-normal-catlinks > ul > li > a', request.source).each(function()
{
console.log("category", $(this).html());
});
}
This works as expected and logs a list of all the category links innerHTML. However the issue happens from that jQuery selector that it tries to load the images that are contained in request.source. This results in errors such as
GET chrome-extension://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Padlock-silver.svg/20px-Padlock-silver.svg.png net::ERR_FAILED
These are valid links, however they are being called (unneeded) from my extension with the chrome-extension:// prefix (which is invalid). I'm not sure why jquery would try to evaluate/request images from within source using a selector
I guess this is happening because Wikipedia uses relative paths on their images (instead of https:// or http://, simply // - so the content loaded is relative to the server). The requests are being made by jQuery and you can see here how to fix this issue (in future, please make sure to search SO more thoroughly).
A huge thank you to #timonwimmer for helping me in the chat. We both happened to find different solutions at the same time.
My solution was to use a regex to remove any occurances of the images. Via
var source = request.source.replace(/.*?\.wikimedia\.org\/.*?/g, "");
His was an answer on stack overflow already, that was derived from another answer. If you are interested this answer works perfectly
If you give jQuery a string with a complete element declaration it actually generates a new DOM element, similar to calling document.createElement(tagName) and setting all of the attributes.
For instance: var $newEl = $("<p>test</p>") or in your case img tag elements with $("<img/>"). That would get parsed and created as a new DOM HTML element and wrapped by jQuery so you can query it.
Since you are passing a complete and valid HTML string, it is parsing it into an actual DOM first. This is because jQuery uses the built in underlying document.querySelector methods and they act on the DOM not on strings -- think of the DOM as a database with indexes for id and class and attributes for querying. For instance, MongoDB cannot perform queries on a raw JSON string, it needs to first process the JSON into BSON and index it all and the queries are performed on that.
Your problem is less with jQuery and more so with how elements are created and what happens when attributes change for those elements. For instance, when the img elements are created with document.createElement('img') and then the src attribute is set with imgElement.src = "link to image" this automatically triggers the load for the image at location src.
You can test this out for yourself by running this in your JavaScript Developer Console:
var img = document.createElement('img');
img.src = "broken-link";
Notice that this will likely show and errors in your console after running stating that the image cannot be found.
So what you want, to ensure so it does not resolve the image's src, is to either
1) apply jQuery on an existing DOM (document.body, etc), or
2) let it parse and evaluate the string into a DOM and clean the string before hand (remove the img tags using Regex or something). Take a look at https://stackoverflow.com/a/11230103/2578205 for removing HTML tags from string.
Hope it works out!
All, we're developing a webapp with AngularJS and we have a use case/requirement (that won't happen very often at all) where we will need to retrieve a complete HTML document from our static server. However, it appears that the $http object returns a raw HTML string as its 'data'. We are trying to avoid using an external library (like jQuery, for instance), but we need to parse that raw HTML string into a queriable DOM object. We could use an iframe and be done with it, but we're also trying to avoid using iframes for well-known reasons.
So, the question is: does AngularJS have a parser for HTML (as it does for JSON)? Or else, what's the most graceful way to handle this case?
P.S.: We tried sifting through Angular's API docs, but in all honesty they are hit-or-miss and unintuitive to navigate around.
If you need to get a DOM element from the string programmatically, parsing html into a DOM object is pretty simple.
Basically you'd use DOMParser.
var parser = new DOMParser();
var doc = parser.parseFromString('<div>some html</div>', 'text/html');
doc.firstChild; //this is what you're after.
From there if you wanted to query to get elements from it with vanilla JS (meaning, you don't want to use JQuery for some reason), you can do this:
//get all anchor tags with class "test" under a div.
var testAnchors = doc.firstChild.querySelectorAll('div a.test');
... but note: querySelectorAll is only supported in IE8 and higher.
EDIT: additional approach...
The "wrap" method:
var div = document.createElement('div');
div.innerHTML = '<div>some html</div>';
var result = div.childNodes;
... do note that this method will return HTMLUnknownElements if you put SVG or MathML into it. They'll "look" normal, but they won't work.
First of all, Angular uses jQuery as well. (jQuery lite)
From FAQ
Does Angular use the jQuery library?
Yes, Angular can use jQuery if it's present in your app when the
application is being bootstrapped. If jQuery is not present in your
script path, Angular falls back to its own implementation of the
subset of jQuery that we call jQLite.
However, I think you don't need full jQuery function here anyway.
That is what I did.
I use a directive and get the html as template and set replace as true.
jsFiddle
<myhtml></myhtml>
angular.module('myApp', []).directive('myhtml', function($http) {
return {
restrict: 'E',
scope: {},
template:'',
link: function(scope, element, attrs) {
$http.post('/echo/json/', data).success(function(re) {
element.html(re.html);
});
}
}
});
Edit note:
I just update the jsFiddle.
I include jquery on top for echoing the request in jsFiddle. In real, you don't need that.
However, it shows you that you can use jQuery in Angular.
If your html doesn't contain any angular tag, this example should work as your expectation .
Otherwise, you need to use compile instead of link.
Using jQuery to create new DOM elements from text.
Example:
jQuery('<div><img src="/some_image.gif"></img></div>');
When this statement is executed, it causes the browser to request the file 'some_img.gif' from the server.
Is there a way to execute this statement so that the resulting jQuery object can be used from DOM traversal, but not actually cause the browser to hit the server with requests for images and other referenced content?
Example:
var jquery_elememnts = jQuery('<div><img class="a_class" src="/some_image.gif"></img></div>');
var img_class = jquery_elememnts.find('img').attr('class');
The only idea I have now is to use regex to remove all of the 'src' tags from image elements and other things that will trigger the browser requests before using jQuery to evaluate the HTML.
How can jQuery be used to evaluate HTML without triggering the browser to make requests to the server for referenced content inside the evaluated HTML?
Thanks!
if you do the regexp way, maybe a simple one like
htmlString.replace(/[ ]src=/," data-src=");
will do the job ?
so instead of looking for :
jquery_elememnts.find('img').attr('src');
you will have to look for :
jquery_elememnts.find('img').data('src');
That's not possible afaik. When the browser loads an HTML fragment, such as one containing an img, which references another resource via src, it will try to fetch it.
However, if you only need to get the class attribute from the img, you can use $.parseXML() to obtain an XMLDocument, and then process it to get the required attribute. This way, the HTML fragment will never be loaded, and thus the image will not be fetched:
var jquery_elememnts = $.parseXML('<div><img class="a_class" src="http://4.bp.blogspot.com/-B6PMBqTyqpk/UC7syX2eRLI/AAAAAAAABL0/SEoLWoxgApo/s1600/google10.png"></img></div>');
var img = jquery_elememnts.getElementsByTagName("img")[0];
var img_class = img.getAttribute("class");
DEMO.
You can use parseXML in jQuery.
var elements = jQuery.parseXML('<div><img class="a_class" src="/some_image.gif"></img></div>');
var img_class = $(elements).find('img').attr('class');
alert(img_class);
The string should be a perfect XML. This is a workaround I used for a similar purpose, but don't know whether this will solve your issue.
var jquery_elememnts = (new DOMParser()).parseFromString('<div><img class="a_class" src="/some_image.gif"></img></div>', 'text/html');
var img_class = jQuery(jquery_elememnts).find('img').attr('class');
At first I made a function that received a parameter and returned jQuery such as:
function getjQuery(window)
{
/*jquery code*/(window);
return window.jQuery;
}
But then I got an email form the review and they told me I have to use jQuery file with the original file name and completely unmodified.
I started to search for an alternative and found this solution, but there is no way it work.
jQuery object is created, but I can't find any elements. $("#id").length is always 0. With the previous method it was always found.
My current code (which doesn't work)
AddonNameSpace.jQueryAux = jQuery;
AddonNameSpace.$ = function(selector,context) {
return // correct window
new AddonNameSpace.jQueryAux.fn.init(selector,context||contentWindow);
};
AddonNameSpace.$.fn =
AddonNameSpace.$.prototype = AddonNameSpace.jQueryAux.fn;
AddonNameSpace.jQuery = AddonNameSpace.$;
The jQuery file is loading on my browser.xul overlay:
<script type="text/javascript" src="chrome://addon/content/bin/jquery-1.5.2.min.js" />
Am I loading in the right place?
How can I use jQuery to modify the content on a page (HTML) with the original jQuery file, is it even possible?
You need pass the e.originalTarget.defaultView on the second parameter on jquery..
If you don't jquery will use window.document, which is the window.document from the xul.
Use
gBrowser.addEventListener("DOMContentLoaded", function (e) {
$("#id", e.originalTarget.defaultView).length
}, true);
instead of
$("#id").length;
And, for avoid conflicts with other extensions don't use script in the xul page, use MozIJSSubScriptLoader.
Components.classes["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
.loadSubScript("chrome://youraddon/content/jquery-1.5.2.min.js");
If you use this method, you load jquery only when you need, avoiding memory leak.
The preferred way to load it is with mozIJSSubScriptLoader so you don't collide with other's extensions. I'm not sure why you're having problems, I can use jQuery in my addon like $("#id").hide() with no additional code (although from the sidebar, now browser.xul).
Either way, this blog post provides a pretty good guide and even has an example xpi to download.
I wrote a simple javascript function that creates a DOM object (in this case a tag ) and I call it in the of my html page and it doesn't seem to work. Any ideas?
function create_link() {
var link = document.createElement("a");
link.setAttribute('href', 'the_link.html');
link.setAttribute('name', 'click on link');
document.childNodes[0].childNodes[1].appendChild(link);
}
The createElement and setAttribute calls are fine, are you sure document.childNodes[0].childNodes[1] is defined?
To test, you can do: document.body.appendChild(link);, which should work.
The problem is likely with document.childNodes[0].childNodes[1]. It's suggested that you use document.getElementById(id) instead, especially since this is more resistant to changes in your HTML structure that may later be made.
In general, avoid using childNodes to navigate to specific parts of the DOM.