How can I capture all the <a> inside an iframe from parent ?

Hello & Thanks in advance ;
I am having the darndest time figuring out how to access the DOM insside an iframe .
How can I capture all the inside an iframe from parent ,
and generally walk my way around the iframe .
Here is my futile attempt at doing so .

<script>
function Function_1(){
console.log("Function-1()")
  //console.log(iframe.document.documentElement.outerHTML)
//  var iframe = iframe.document.querySelector("button");
// var iframe = document.body
//  console.log(document.body.iframe)
var iframe = document.querySelector("iframe");
  console.log(iframe)
  console.log(document.iframe)
  console.log(iframe.document.querySelectorAll("a"));
    console.log(iframe);
  iframe = iframe.document.documentElement
  console.log(iframe);
  document.getElementById("paragraph_1").innerHTML = iframe ;
}
</script>

Is the content inside your iframe coming from the same domain as the outer document?

Yes , same domain always .

And I am trying unsuccessfully to querySelect a in the iframe .

function Function_1(){
console.log("Function-1()")
const iFrame1 = document.querySelector("iFrame");
  console.log(iFrame1)
const iFrame1Body = iFrame1.Document.querySelector("a") ;
  console.log(iFrame1Body)
}

No probs with ‘querySelector(“iFrame”)’ .

But with ‘querySelector(“iFrame”)’ I get error ‘Uncaught TypeError: Cannot read property ‘querySelector’ of undefined’ .

Pls , what am I doing wrong ?

Thanks for your Help…

This should work:
var iframe1 = document.querySelector(“iframe”);
var atags= iframe1.contentWindow.document.querySelectorAll(“a”);
for(let i of atags){i.callMethod}

Thanks steve ,
I’ll study this a while :slightly_smiling_face:

Hello ,
I am getting this error:
Uncaught TypeError: Cannot read property ‘contentWindow’ of null
at WIP-ThisShouldWork-Stripped.html:25
This is line 25:
var allTags= iframe1.contentWindow.document.querySelectorAll("a");
I don’t see what is wrong :upside_down_face:?
Full code:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Opening Links in an iFrame</title>
	<style>
		iframe {
			width: 100%;
			height: 500px;
		}
		button {display: inline-block;}
		iframe { width="50%";}
	</style>
</head>
<body>
 <p style="text-align: center;">Communiation Between 'parent' and 'iframe Child' elements
<br>
<button  onclick="Function_1()">1</button>
<button  onclick="Function_2()">2</button>
<button  onclick="Function_3()">3</button>
<button  onclick="Function_4()">4</button>
	</p>
    <iframe id="iframe1" src="SomeLocalLinks-01.html" name="iFrame1"></iframe>
<script>
var iframe1 = document.querySelector("iframe1");
var allTags= iframe1.contentWindow.document.querySelectorAll("a");

console.log(allTags)
for(let i of allTags){i.callMethod}
</script>
<script>
	var links = iFrame1.document.querySelectorAll( 'a' );
	for ( var c = 0; c < links.length; c ++ ) {
         links[c].addEventListener('click', WhichLinkWasClicked);
      }
function WhichLinkWasClicked(evt) {
    alert( evt.target ) ; 
    evt.preventDefault();
		}
</script>
<script>
function Function_1(){
console.log("Function-1()")
}
</script>
<script>
function Function_2(){
alert("Function-2()")}
</script>
<script>
function Function_3(){
alert("Function-3()")}
</script>
<script>
function Function_4(){
alert("Function-4()")}
</script>
</body></html>

Thanks

Are you trying to change the page inside the iframe based on what button was clicked -> eg. home page, about page, contact page… or are you trying to return a list of links inside the iframe every time you click a button -> e.g. click music button, display list of audio links, click video button display list of youtube links…

steve ,
I am trying to return a list of links inside the iframe every time I click on a Link .
When I can successfully reference iframe1 , then I’ll need to create an iframe1 addEventListener for clicks .
I am puzzled why any attempt to reference iframe1 returns null .

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Opening Links in an iFrame</title>
	<style>
		iframe {
			width: 100%;
			height: 500px;
		}
		button {display: inline-block;}
		iframe { width="50%";}
	</style>
</head>
<body>
 <p style="text-align: center;">Communiation Between 'parent' and 'iframe Child' elements</p>
    <iframe id="iframe1" src="SomeLocalLinks-01.html" name="iFrame1"></iframe>

<script>
var iframe1 = [] 
iframe1 = document.querySelector("iframe1");
console.log(iframe1)
var allTags= iframe1.contentWindow.document.querySelectorAll("a");
console.log(allTags)
for(let i of allTags){i.callMethod}
Function callMethod(){
}
</script>

<script>
	var links = iFrame1.document.querySelectorAll( 'a' );
	for ( var c = 0; c < links.length; c ++ ) {
         links[c].addEventListener('click', WhichLinkWasClicked);
      }
function WhichLinkWasClicked(evt) {
    alert( evt.target ) ; 
    evt.preventDefault();
		}
</script>
</body></html>

Thanks

This might work. I don’t think you can call functions from the iframe without using window.parent to access the parents scope , if its cross origin you have to postmessage() to send strings, you can then use a switch statement to call functions.

function WhichLinkWasClicked(evt) {
alert( evt.target ) ; 
evt.preventDefault();		

}
const load = () => {
var iframe1 = document.querySelector(“iframe1”).contentWindow.document;
var allTags= iframe1.querySelectorAll(“a”);
console.log(allTags);
for(let i of allTags){i.addEventListener(‘click’, WhichLinkWasClicked)}
}
document.addEventListener(‘DOMContentLoaded’, load);
function Function_1(){
alert(“Function-1()”)
}

function Function_2(){
alert(“Function-2()”)}

function Function_3(){
alert(“Function-3()”)}

function Function_4(){
alert(“Function-4()”)}

I’m still gettimg an error on:
var iframe1 = document.querySelector(“iframe1”).contentWindow.document;
Uncaught SyntaxError: Invalid or unexpected token

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Opening Links in an iFrame</title>
	<style>
		iframe {
			width: 100%;
			height: 500px;
		}
		button {display: inline-block;}
		iframe { width="50%";}
	</style>
</head>
<body>
    <p style="text-align: center;">Communiation Between 'parent' and 'iframe Child' elements
<button  onclick="Function_1()">1</button>
<button  onclick="Function_2()">2</button>
<button  onclick="Function_3()">3</button>
<button  onclick="Function_4()">4</button>
	<p id="paragraph_1">  empty  </p>
    <iframe id="iframe1" src="SomeLocalLinks-01.html" name="iFrame1"></iframe>
<script>
const load = () => {
var iframe1 = document.querySelector(“iframe1”).contentWindow.document;
console.log(iframe1);
var allTags= iframe1.querySelectorAll(“a”);
console.log(allTags);
for(let i of allTags){i.addEventListener(‘click’, WhichLinkWasClicked)}
}
document.addEventListener(‘DOMContentLoaded’, load);

function Function_1(){
alert(“Function-1()”)
}

function Function_2(){
alert(“Function-2()”)}

function Function_3(){
alert(“Function-3()”)}

function Function_4(){
alert(“Function-4()”)}</script>
</body></html>

Oops ! Each time I enter your code I get errors because of your Quotes .
My system is windows , so I have to change them .
There is an error , but its not that . I’m composing a new msg , hold on :slight_smile:

This is the error I am getting:
Uncaught TypeError: Cannot read property ‘contentWindow’ of null
at HTMLDocument.load
It dosen’t like ‘contentWindow’ , I tried ‘contentDocument’ , it doesn’t like that either .

woops

var iframe1 = document.querySelector(“#iframe1”).....
or var iframe1 = document.getElementById('iframe1').......

Hi steve ,
Hmm… Making headway , showing iframe elements :slight_smile:
But I’m still not collecting allTags .
Node list is still showing 0 , don’t know why .
Code:

    const load = () => {
//var iframe1 = document.querySelector(“iframe1”).contentWindow.document;
var iframe1 = document.getElementById('iframe1')
console.log("iframe1");
console.log(iframe1);
var allTags= iframe1.querySelectorAll("a");
console.log("allTags");
console.log(allTags);

function WhichLinkWasClicked(evt) {
alert( evt.target ) ; 
evt.preventDefault();	
}
console.log("for(let i of allTags){1}")
for(let i of allTags){
i.addEventListener('click', WhichLinkWasClicked)
console.log("for(let i of allTags){2}")
console.log(i.allTags) ; }
}
document.addEventListener('DOMContentLoaded', load);

Console:

WIP-Parent-ThisShouldWork-Steve.html:
26 iframe1
WIP-Parent-ThisShouldWork-Steve.html:
27 
<iframe id=​"iframe1" src=​"SomeLocalLinks-01.html" name=​"iFrame1">​

#document

<!DOCTYPE html>
<html lang=​"en">​
<head>​…​
</head>​
<body>​
<h1 style=​"text-align:
​ center">​01 Same-Domain , Same Folder​
</h1>​
<br>​
<br>​
<a href=​"SomeLocalLinks-01.html">​SomeLocalLinks-01​
</a>​
<br>​
<br>​
<a href=​"SomeLocalLinks-02.html">​SomeLocalLinks-02​
</a>​
<br>​
<br>​
<a href=​"SomeLocalLinks-03.html">​SomeLocalLinks-03​
</a>​
<br>​
<br>​
<a href=​"SomeLocalLinks-04.html">​SomeLocalLinks-04​
</a>​
<br>​
<br>​
</body>​
</html>​
</iframe>​
WIP-Parent-ThisShouldWork-Steve.html:
29 allTags
WIP-Parent-ThisShouldWork-Steve.html:
30 
NodeList []length:
 0__proto__:
 NodeListentries:
 ƒ entries()forEach:
 ƒ forEach()item:
 ƒ item()keys:
 ƒ keys()length:
 (...)values:
 ƒ values()constructor:
 ƒ NodeList()Symbol(Symbol.iterator):
 ƒ values()Symbol(Symbol.toStringTag):
 "NodeList"get length:
 ƒ length()__proto__:
 Object
WIP-Parent-ThisShouldWork-Steve.html:
36 for(let i of allTags){1}

Thanks

Hey mate,
If your iframe is same-origin,what do you want the iframe for? CSS encapsulation? JS encapsulation?
Iframes were designed to sandbox script execution (not talk to your page). Some browsers treat CORs differently (firefox considers my local host/ same folder cross- origin)
If you want style and JS encapsulation shadow DOM with ES6 modules (similar to IIFE) is a better way to do it.
I even have a inline script module that opens a shadow root mode closed (all outside code returns null, even the console) on the <body> with a variable reference to the root contained in the module, attaches itself to the shadow root, appends all html <template> to the root and sets up a mutation observer that removes all added elements or attribute changes outside of the shadow root. After you freeze Element.prototype apart from changing attributes to the <head> elements, deleting the <body> or using document.write() (your can modify any native method to return undefined and object.freeze() the protoype if you are that paranoid), there isn’t much you can do to tamper with the page and nothing can interact with the shadow root except the inline module inside the root.

steve ,
I think you are saying :
iframe is an encapsulation .
So parent and iframe are not same-origin .
So I can’t get access into iframe without dabbling with Shadow root .
Do I have that right ?

What I am trying to do is :
Collect a list of all <a>s in iframe .
compare that list against a list of safeSites .
If that <a> is in the list allow the Navigation .
If that <a> is not in the list do event.preventDefault() .

No mate, what I am saying is that if the browser thinks that the <iframe> is from a different site or server it will hide all the properties of the iframe.contentDocument and getElementById(), querySelector(), querySelectorAll() will return undefined or an empty nodelist.
quick reference video -> https://www.youtube.com/watch?v=-mNp3-UX9Qc
If your <iframe> is from the same server, you don’t need it in an <iframe> (because you trust your content).
If you want to run a function that checks all your <a> on your page against a list of safe sites, do that (maybe prevent link clicking until the function has run or dynamically add links to the page after they have been checked).
If you want to open content from another site (your links) inside an <iframe> this video will show you how -> https://www.youtube.com/watch?v=cmA-IyD8_BA;
This is a modified version of the code part you want.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>First Window</title>
    <style>
      iframe {border: 1px solid red;}
      button { margin: 1rem; }
    </style>
  </head>
  <body>
    <iframe
      id="fr"
      name="myFrame"
      width="400"
      height="400"
      src="about:blank"
    ></iframe>
    <br>
    <a id="btnFrame" href="https://www.ntnews.com.au/">The NT news</a>
    <script>
  let other = null; //will be our window reference
  let features =
    'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes';
  //,height=400,width=400

  const runCheck = (x) => { return new Promise((resolve) => { return setTimeout(() => resolve(), 3000 )})}

    const checkThenDisplay = async function(ev){
        let url = ev.target.href;
        let check = await runCheck(url);
        let other = window.open(url, 'myFrame');
    }
document.getElementById('btnFrame').addEventListener('click', (ev) => {
    event.preventDefault();
    checkThenDisplay(ev)
  });
  
</script>
  </body>
</html>

Quick note, some pages will not allow you to open them inside an <iframe> e.g google.com and also sandbox your iframe however you see fit…

Side note if your checking function is async (relies on fetch, import ect) just await the fetch() not runCheck().
If not async, inline your code in checkThenDisplay() and drop the async.