admin管理员组文章数量:1415100
- I need to inject an element inside an
<iframe>
, after a<button>
within this iframe is clicked. - This
<iframe>
exists within a page (e. g. somewebpage) that is not under my control. - The
<iframe>
's content belongs to the same domain as the main document's body/DOM structure. - To achieve this I need to use a Content Script file from a Chrome Extension as per the below V3 manifest's
"content_scripts"
configuration:
"content_scripts": [
{
"type": "module", // I added this items "type", "run_at", "all_frames"
"run_at": "document_end", // and "match_origin_as_fallback" hoping they
"all_frames": true, // would help.
"match_origin_as_fallback": true,
"js": [
"foreground.js" // File containing the pure JS code
],
"matches": [
"/*" // The page where I want to inject the element
]
The extension's content script "foreground.js"
file contains the code that should detect the iframe, listen to the click
event, and then inject the element within the iframe after click
is detected.
After the page is fully loaded, when I inspect the elements on this page, it basically looks like this:
<body>
<macroponent>
#shadow-root (open) <!-- Shadow DOM element. I believe this is what is my main obstacle -->
<div>
<sn-canvas-appshell-root>
<sn-canvas-appshell-layout>
<sn-polaris-layout>
<iframe id="myIframe"> <!-- to access button I need to find the iframe 1st -->
<button id="myButton">TAKE</button> <!-- I need to get that click event -->
</iframe>
</sn-polaris-layout>
</sn-canvas-appshell-root>
</sn-canvas-appshell-root>
</div>
</macroponent>
</body>
I have tried a lot of different approaches and none were able to find the <iframe>
. The error messages on the console are returning the getElementById
as null
and getElementsByTagName
as undefined
.
As a standard method I tried this with some variations like window.onload
:
document.onreadystatechange = () => {
if (document.readyState === 'plete') {
let iframe = document.getElementsByTagName('iframe')[0];
iframe.contentDocument.getElementById("myButton").addEventListener("click", function () {
alert("Hurray!");
// Injected element code here
});
}
I have also tried some more advanced solutions like this:
function searchFrame(id) { // id = the id of the wanted (i)frame
var result = null, // Stores the result
search = function (iframes) { // Recursively called function
var n; // General loop counter
for (n = 0; n < iframes.length; n++) { // Iterate through all passed windows in (i)frames
if (iframes[n].frameElement.id === id) { // Check the id of the (i)frame
result = iframes[n]; // If found the wanted id, store the window to result
}
if (!result && iframes[n].frames.length > 0) { // Check if result not found and current window has (i)frames
search(iframes[n].frames); // Call search again, pass the windows in current window
}
}
};
search(window.top.frames); // Start searching from the topmost window
return result; // Returns the wanted window if found, null otherwise
}
Source:
Get element value inside iframe which is nested inside Frame in javascript?
In my opinion, this issue persists perhaps because the <iframe>
is a child to a "Shadow DOM" element, which makes the method of finding the <iframe>
different from the standard.
After realizing that probably the issue is that the <iframe>
is a child to a "Shadow DOM" element, I tried this approach from another question:
this.windows = this.shadowRoot.getElementsByTagName('app-window')
Source:
GetElementById from within Shadow DOM
Or even this one:
(although I suspect it might be a bit overkill for my use case)
customElements.define("my-ponent", class extends HTMLElement {
constructor() {
super().attachShadow({mode:"open"}).innerHTML = `<slot></slot>`;
}
})
const shadowDive = (
el,
selector,
match = (el, root) => {
console.warn('match', el, root);
},
root = el.shadowRoot || el
) => {
root.querySelector(selector) && match(root.querySelector(selector), root);
[...root.querySelectorAll("*")].map(el => shadowDive(el, selector, match));
}
shadowDive(document.body, "content"); // note optional parameters
Source:
How to select element tag from shadow root
And again I am getting the same null
or undefined
errors.
But I think that perhaps I am on the right track now that I know that the <iframe>
is nested within a "Shadow DOM" element.
Perhaps there is a way to bypass or respect the "Shadow DOM" and get to that <iframe>
but to be honest I am out of ideas.
Thank you
- I need to inject an element inside an
<iframe>
, after a<button>
within this iframe is clicked. - This
<iframe>
exists within a page (e. g. somewebpage.) that is not under my control. - The
<iframe>
's content belongs to the same domain as the main document's body/DOM structure. - To achieve this I need to use a Content Script file from a Chrome Extension as per the below V3 manifest's
"content_scripts"
configuration:
"content_scripts": [
{
"type": "module", // I added this items "type", "run_at", "all_frames"
"run_at": "document_end", // and "match_origin_as_fallback" hoping they
"all_frames": true, // would help.
"match_origin_as_fallback": true,
"js": [
"foreground.js" // File containing the pure JS code
],
"matches": [
"https://somewebpage./*" // The page where I want to inject the element
]
The extension's content script "foreground.js"
file contains the code that should detect the iframe, listen to the click
event, and then inject the element within the iframe after click
is detected.
After the page is fully loaded, when I inspect the elements on this page, it basically looks like this:
<body>
<macroponent>
#shadow-root (open) <!-- Shadow DOM element. I believe this is what is my main obstacle -->
<div>
<sn-canvas-appshell-root>
<sn-canvas-appshell-layout>
<sn-polaris-layout>
<iframe id="myIframe"> <!-- to access button I need to find the iframe 1st -->
<button id="myButton">TAKE</button> <!-- I need to get that click event -->
</iframe>
</sn-polaris-layout>
</sn-canvas-appshell-root>
</sn-canvas-appshell-root>
</div>
</macroponent>
</body>
I have tried a lot of different approaches and none were able to find the <iframe>
. The error messages on the console are returning the getElementById
as null
and getElementsByTagName
as undefined
.
As a standard method I tried this with some variations like window.onload
:
document.onreadystatechange = () => {
if (document.readyState === 'plete') {
let iframe = document.getElementsByTagName('iframe')[0];
iframe.contentDocument.getElementById("myButton").addEventListener("click", function () {
alert("Hurray!");
// Injected element code here
});
}
I have also tried some more advanced solutions like this:
function searchFrame(id) { // id = the id of the wanted (i)frame
var result = null, // Stores the result
search = function (iframes) { // Recursively called function
var n; // General loop counter
for (n = 0; n < iframes.length; n++) { // Iterate through all passed windows in (i)frames
if (iframes[n].frameElement.id === id) { // Check the id of the (i)frame
result = iframes[n]; // If found the wanted id, store the window to result
}
if (!result && iframes[n].frames.length > 0) { // Check if result not found and current window has (i)frames
search(iframes[n].frames); // Call search again, pass the windows in current window
}
}
};
search(window.top.frames); // Start searching from the topmost window
return result; // Returns the wanted window if found, null otherwise
}
Source:
Get element value inside iframe which is nested inside Frame in javascript?
In my opinion, this issue persists perhaps because the <iframe>
is a child to a "Shadow DOM" element, which makes the method of finding the <iframe>
different from the standard.
After realizing that probably the issue is that the <iframe>
is a child to a "Shadow DOM" element, I tried this approach from another question:
this.windows = this.shadowRoot.getElementsByTagName('app-window')
Source:
GetElementById from within Shadow DOM
Or even this one:
(although I suspect it might be a bit overkill for my use case)
customElements.define("my-ponent", class extends HTMLElement {
constructor() {
super().attachShadow({mode:"open"}).innerHTML = `<slot></slot>`;
}
})
const shadowDive = (
el,
selector,
match = (el, root) => {
console.warn('match', el, root);
},
root = el.shadowRoot || el
) => {
root.querySelector(selector) && match(root.querySelector(selector), root);
[...root.querySelectorAll("*")].map(el => shadowDive(el, selector, match));
}
shadowDive(document.body, "content"); // note optional parameters
Source:
How to select element tag from shadow root
And again I am getting the same null
or undefined
errors.
But I think that perhaps I am on the right track now that I know that the <iframe>
is nested within a "Shadow DOM" element.
Perhaps there is a way to bypass or respect the "Shadow DOM" and get to that <iframe>
but to be honest I am out of ideas.
Thank you
Share Improve this question asked Oct 27, 2022 at 16:40 VerminousVerminous 5555 silver badges21 bronze badges1 Answer
Reset to default 5- A shadowRoot doesn't have
getElementsByTagName
. - ShadowDOM frames aren't exposed in the global
window
orframes
(it's the same thing BTW) - customElements.define doesn't work inside content scripts (due to "world isolation").
You can use querySelector + contentDocument:
const el = document.querySelector('macroponent').shadowRoot.querySelector('iframe');
const elInner = el.contentDocument.querySelector('div');
Note that you don't need all_frames
or match_origin_as_fallback
here because the iframe is same-origin, hence it's directly accessible from the main document's content script as shown above.
One pitfall is that modern sites generate the contents of the page dynamically, often depending on a network response, which can happen long time after all your content scripts ran. In this case you can use MutationObserver to detect it or simply re-check periodically inside setInterval.
本文标签:
版权声明:本文标题:javascript - How to capture iframe nested within Shadow DOM's "#shadow-root (open)" element? - Stack O 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745164105a2645589.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论