I've been trying to make an extension that gets the source of video players on the web by looking at the iframe sources, however it turns out a lot of those iframes have iframes nested inside of them where the actual video is, or sometimes it's even another iframe deep. I've been trying to look deeper with things js like this:
var iframe = document.getElementsByTagName("iframe");
var nestedframes = iframe.getElementsByTagName("iframe");
and like this:
var iframe = document.getElementsByTagName("iframe");
var innerDoc = iframe.contentWindow.webbody.innerHTML;
var nestedframes = innerDoc.getElementsByTagName("iframe");
but they return this error: contentscript.js:6 Uncaught TypeError: iframe.getElementsByTagName is not a function.
If anyone has any ideas that would be greatly appreciated.
getElementsByTagName() method gets all elements on the document with the specified tag name. It means that it gives you a list of elements.
So you should itearate over it or select single element to call another DOM method. (Notice [0])
var iframe = document.getElementsByTagName("iframe")[0];
var nestedframes = iframe.getElementsByTagName("iframe");
Related
I have a functionality in my system that transcripts from voice to text using an external library.
This is what the library renders:
What I need is really simple: to get the text from the generated textareas.
The textareas are rendered without any name or id, so I can only access them by class in the Google Chrome console. Whenever I try to get them by class in my javascript code, I get an array of [0] elements.
I think that the problem is that this library renders a new #document and I'm not able to get it's content in my $(document).ready function because it scopes the 'parent' document.
How it renders.
Any thoughts on this? Thank you.
I hope the code below helps.
// Get you iframe by Id or other way
let iframe = document.getElementById("myFrame");
// After iframe has been loaded
iframe.onload= function() {
// Get the element inside your iframe
// There are a lot of ways to do it
// It is good practice to store DOM objects in variables that start with $
let $elementByTag = iframe.contentWindow.document.getElementsByTagName("p")[0];
let $elementById = iframe.contentWindow.document.getElementById("elementId");
let $elementByClass = iframe.contentWindow.document.getElementsByClassName("classHere");
let $elementBySelector = iframe.contentWindow.document.querySelector("#dad .classname");
// After get the element extract the text/html
let text = $element.innerText
let html = $element.innerHTML
};
I have two documents, one of which is embedded into the other with an iframe. I'm trying to use this code to access the img tag inside the iframe, however, I cannot use the document.getElementById function on the iframe element:
iframes = document.getElementsByTagName("iframe")
spaces = iframes[0].contentWindow.document;
spaces = spaces.getElementsByTagName("img")
for (var i=0, max=spaces.length; i < max; i++) {
alert(spaces[i].innerHTML)
}
alert(spaces)
var x = document.getElementById("frame");
var y = (x.contentWindow || x.contentDocument);
if (y.document)y = y.document;
alert(y.body.innerHTML)
spaces returns [object HTMLCollection] and y returns [object HTMLDocument]
So, if you're on the same domain as your iframe content, you can use
document.getElementById('my-iframe').contentWindow.document.getElementById(...)
If you're not on the same domain, you can't access/manipulate iframe content for security reasons. Otherwise malicious folks could throw up an iframe that looks like Facebook or something and steal other folk's info.
If you're not on the same domain but own both domains you're using, you can set up messaging between the two frames. And use the parent to message the child for information/manipulation.
I just want to replace a blank iframe's document with a pre-existing document object. Not a string of HTML that can use the write() method with (that solution is in many other answers, here). I just want a document object to replace another document.
My blank iframe:
<iframe src="about:blank" id="myIframe"></iframe>
Routine to replace blank iframe document:
function replaceIframeDoc(objDoc){
var nod_if = document.getElementById("myIframe");
var nod_ifDoc = nod_if.contentWindow.document || nod_if.contentDocument;
nod_ifDoc = objDoc;
}
Document object created, and call to do the replace:
(Note: I encapsulate my document object in a simple object... I need to do that for some other unrelated stuff... just to easily transfer some attributes, etc. Hopefully it's inconsequential to my issue!)
var obj_container = new Object; //just to make easier to do some other stuff
obj_container.document = document.implementation.createHTMLDocument();
obj_container.document.open();
obj_container.document.write("<html>...(some html)...</html>");
obj_container.document.close();
replaceIframeDoc(obj_container.document);
Depending on which browser you need to support, you could do something like this:
var iframe = document.getElementById("myIframe");;
var oldNode = iframe.contentDocument.getElementById("myNode");
var newNode = document.importNode(oldNode, true);
document.getElementById("container").appendChild(newNode);
You can read more here: https://developer.mozilla.org/en-US/docs/Web/API/document.importNode
I'm working on Chrome extension and I have following problem:
var myDiv = document.createElement('div');
myDiv.innerHTML = '<img src="a.png">';
What happens now is that Chrome tries to load the "a.png" resource, even If I don't attach the "div" element to document. Is there a way to prevent it?
_In the extension I need to get data from a site that doesn't provide any API, so I have to parse the whole HTML to get the necessary data. Writing my own simple HTML parser could be tricky so I would rather use the native HTML parser. However, in Chrome when I put the whole source code to some temporary non-attached element (so it would get parsed and I could filter the necessary data), ale the images (and possibly other resources) start to load as well, causing higher traffic or (in case of relative paths) lots of errors in console. _
To prevent the resources from being loaded, you'll need to create your Node in an entirely new #document. You can use document.implementation.createHTMLDocument for this.
var dom = document.implementation.createHTMLDocument(); // make new #document
// now use this to..
var myDiv = dom.createElement('div'); // ..create a <div>
myDiv.innerHTML = '<img src="a.png">'; // ..parse HTML
You can delay parsing/loading html by storing it in non-standard attribute, then assigning it to innerHtml, "when the time comes":
myDiv.setAttribute('deferredHtml', '<img src="http://upload.wikimedia.org/wikipedia/commons/4/4e/Single_apple.png">');
global.loadDeferredImage = function() {
if(myDiv.hasAttribute('deferredHtml')) {
myDiv.innerHTML = myDiv.getAttribute('deferredHtml');
myDiv.removeAttribute('deferredHtml');
}
};
... onclick="loadDeferredImage()"
I created jsfiddle illustrating this idea:
http://jsfiddle.net/akhikhl/CbCst/3/
I am requesting full HTML5 documents via Ajax using jQuery. I want to be able to parse them and transfer elements to my main page DOM, ideally with all major browsers, including mobile. I don't want to create an iframe as I want the process to be as quick as possible. With Chrome & Firefox I can do the following:
var contents = $(document.createElement('html'));
contents[0].innerHTML = data; // data : HTML document string
This will create a proper document, somewhat surprisingly, just without a doctype. In IE9, however, one may not use the innerHTML to set the contents of the html element. I tried to do the following, without any luck:
Create a DOM, open it, write to it and close it. Issue: on doc.open, IE9 throws an exception called Unspecified error..
var doc = document.implementation.createHTMLDocument('');
doc.open();
doc.write(data);
doc.close();
Create an ActiveX DOM. This time, the result is better but upon transferring / copying elements between documents IE9 crashes. Bad because no IE8 support (adoptNode / importNode support).
var doc = new ActiveXObject('htmlfile');
doc.open();
doc.write(data);
doc.close();
contents = $(doc.documentElement);
document.adoptNode(contents);
I was thinking about recursively recreating the elements, instead of transferring them between my documents, but that seems like an expensive task, given that I can have a lot nodes to transfer. I like my last ActiveX example as that will most likely work in IE8 and earlier (for parsing, at least).
Any ideas on this? Again, not only I need to be able to parse the head and body, but I also need to be able to append these new elements to my main dom.
Thanks much!
Answering my own question... To solve my issue I used all solutions mentioned in my post, with try/catch blocks if a browser throws an error (oh, how we love thee IE!). The following works in IE8, IE9, Chrome 23, Firefox 17, iOS 4 and 5, Android 3 & 4. I have not tested Android 2.1-2.3 and IE7.
var contents = $('');
try {
contents = $(document.createElement('html'));
contents[0].innerHTML = data;
}
catch(e) {
try {
var doc = document.implementation.createHTMLDocument('');
doc.open();
doc.write(data);
doc.close();
contents = $(doc.documentElement);
}
catch(e) {
var doc = new ActiveXObject('htmlfile');
doc.open();
doc.write(data);
doc.close();
contents = $(doc.documentElement);
}
}
At this point we can find elements using jQuery. Transferring them to a different DOM creates a bit of a problem. There are a couple of methods that do this, but they are not widely supported yet (importNode & adoptNode) and/or are buggy. Given that our selector string is called 'selector', below I re-created the found elements and append them to '.someDiv'.
var fnd = contents.find(selector);
if(fnd.length) {
var newSelection = $('');
fnd.each(function() {
var n = document.createElement(this.tagName);
var attr = $(this).prop('attributes');
n.innerHTML = this.innerHTML;
$.each(attr,function() { $(n).attr(this.name, this.value); });
newSelection.push(n);
});
$('.someDiv').append(newSelection);
};