【问题标题】:Problem selecting element in an iframe booking form在 iframe 预订表单中选择元素时出现问题
【发布时间】:2020-05-03 20:32:00
【问题描述】:

我正在尝试选择此 iframe 预订表单中的电子邮件字段。我最终想对该字段做其他事情,但现在作为测试,我只想选择元素并更改占位符。

收到此错误,所以我没有正确选择它: 未捕获的类型错误:无法在 HTMLButtonElement.changeCopy 处设置 null 的属性“占位符”

您可以在此处查看我的代码的实时版本,并在单击顶部按钮时在控制台中查看错误:https://finnpegler.github.io/cart_recover/

我还在下面将代码作为 sn-p 包含在内,但它会引发与跨源帧有关的不同错误。

var iframe = document.getElementById("booking-widget-iframe");
var field = iframe.contentWindow.document.querySelector("booking[email]");

function changeCopy() {
field.placeholder = "hello";
}

document.getElementById("button").addEventListener("click", changeCopy)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test site for Cart Recover Tool</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheet.css" />
<link href="https://fonts.googleapis.com/css?family=Bree+Serif|Open+Sans&display=swap" rel="stylesheet">
<link rel="icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
</head>
<body>
Clicking this button should change the placeholder text in the email field below
<button id = "button">Click</button>
</body>

<script src="https://bettercleans.launch27.com/jsbundle"></script><iframe id="booking-widget-iframe" src="https://bettercleans.launch27.com/?w_cleaning" style="border:none;width:100%;min-height:2739px;overflow:hidden" scrolling="no"></iframe>
<script src="script.js"></script>

【问题讨论】:

  • 浏览器将阻止父框架试图操纵子框架的内容或结构,如果它们不是同源,即不同域。在您的情况下,父框架来源是 finnpegler.github.io 但子框架来源是 bettercleans.launch27.com。如果您可以访问 bettercleans.launch27.com 的 js 源代码,您可以尝试通过向子框架发布消息来解决它。看看这篇 SO 帖子是否对您有帮助 stackoverflow.com/questions/25098021/…

标签: javascript dom iframe


【解决方案1】:

首先让我们弄清楚到底发生了什么。为了帮助分析,我修改了您的脚本,但没有引入任何副作用,如下所示:

var iframe = document.getElementById( "booking-widget-iframe" );

console.log( `iframe: ${ iframe }` );
console.log( `iframe.contentWindow: ${ iframe.contentWindow }` );
console.log( `iframe.contentWindow.document: ${ iframe.contentWindow.document }` );
console.log( `iframe.contentWindow.document.querySelector("booking[email]"): ${ iframe.contentWindow.document.querySelector( "booking[email]" ) }` );

function changeCopy() {
    console.log( "-------------------------------------------------------------------------------" );
    console.log( "changeCopy: started" );
    console.log( "-------------------------------------------------------------------------------" );

    var iframe = document.getElementById( "booking-widget-iframe" );

    console.log( `iframe: ${ iframe }` );
    console.log( `iframe.contentWindow: ${ iframe.contentWindow }` );
    console.log( `iframe.contentWindow.document: ${ iframe.contentWindow.document }` );
    console.log( `iframe.contentWindow.document.querySelector("booking[email]"): ${ iframe.contentWindow.document.querySelector( "booking[email]" ) }` );

    var field = iframe.contentWindow.document.querySelector( "booking[email]" );
    field.placeholder = "hello";

    console.log( "-------------------------------------------------------------------------------" );
    console.log( "changeCopy: finished" );
    console.log( "-------------------------------------------------------------------------------" );
}

document.getElementById( "button" ).addEventListener( "click", changeCopy )

使用上述脚本加载文档证明了以下结果:

我们从输出中学到了什么?

  1. 在单击按钮之前,iframe.contentWindow.document.querySelector( "booking[email]" ) 的计算结果为 null。因此变量field本质上是null

  2. 为什么iframe.contentWindow.document.querySelector( "booking[email]" ) 在按钮点击之前评估为null

    • 由于 iframe 的文档尚未加载,浏览器 (Safari) 认为在父框架和子框架之间运行安全策略不是最佳选择。
  3. 当我按下 Click 按钮时,#booking-widget-iframe(子框架)的 document 已完全加载,并且在父框架和子框架之间执行安全策略已启用。因此,由于未能遵守跨域框架策略,访问iframe.contentWindow 的尝试立即被阻止。

可能的解决方案:

  • 为了允许父框架中的脚本访问子框架中的对象,父框架和子框架都应该从相同的domain(即https://bettercleans.launch27.com)和port(与所考虑的情况无关)。
  • 或者,使用 发布消息 在两个框架之间进行交互,如 Window.postMessage() 中所述。

假设您可以应用该解决方案,应该会弹出另一个问题?。 iframe.contentWindow.document.querySelector("booking[email]") 仍将是 null。后续问题的根本原因是在目标文档中找不到名为 booking 的元素。也就是说,选择器"booking[email]"是错误的。

【讨论】:

    【解决方案2】:

    field 之所以为null,是因为在设置变量的时候,值被阻塞了。 尝试在页面上打开 JavaScript 控制台并粘贴

    var iframe = document.getElementById("booking-widget-iframe");
    

    就像在源代码中一样,它应该可以工作,但然后看看当你输入时会发生什么:

    var field = iframe.contentWindow.document.querySelector("booking[email]");
    

    你应该得到类似以下的错误:

    Uncaught DOMException: Blocked a frame with origin "https://finnpegler.github.io" from accessing a cross-origin frame.
        at <anonymous>:1:34
    

    所以问题不在于field.placeholder = "hello"; 为空,而是该字段从未设置,因为它被跨域源阻止。

    解决方法: 如果您有权访问https://bettercleans.launch27.com/?w_cleaning&iframe_id=j8szoz0b4,则在该页面源的某处,输入以下脚本:

    <script>
    addEventListener("message", function(e) {
        console.log(e.data);
        if(e.data["setIt"]) {
            document.querySelector("booking[email]").placeholder = e.data["setIt"];
             e.source.postMessage({
                 hi:"I did it" 
             });
        }
    
    });
    
    
    </script>
    

    然后在您的https://finnpegler.github.io/cart_recover/页面源某处输入代码:

    <script>
    var iframe;
    addEventListener("load", function() {
        iframe = document.getElementById("booking-widget-iframe");
        iframe.contentWindow.postMessage({
            setIt: "hello"
        });
    
    });
    addEventListener("message", function(e) {
        console.log(e);
    })
    </script>
    

    警告:未经测试的代码,但基本思想如下: 使用客户端 JavaScript,您不能直接编辑 iframe 的元素,因此与其通信的方式是使用 iframe.contentWindow.postMessage 向 iframe 发送消息。在 iframe 方面,您可以读取与事件侦听器“消息”一起传入的消息(或者您可以执行 window.onmessage)。然后您可以将 JSON 数据或文本作为消息发送到 iframe,并使用 e.data 读取它。 所以在上面(未经测试)的例子中,当主 github 页面想要将占位符的值更改为特定数量时,它只是将包含占位符数据的 JSON 对象发送到 iframe,然后在 iframe 源端,它读取该传入消息,检查 JSON 是否具有用于设置占位符的设置键,如果具有该键,则将占位符的值设置为该属性的值。

    如果您还有其他问题,请告诉我。

    以下是一些参考资料:

    https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage https://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage https://javascript.info/cross-window-communication https://bl.ocks.org/pbojinov/8965299 https://www.ilearnjavascript.com/plainjs-postmessage-and-iframes/ https://medium.com/@Farzad_YZ/cross-domain-iframe-parent-communication-403912fff692

    【讨论】:

      猜你喜欢
      • 2020-12-10
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多