【问题标题】:Move entire document into iframe将整个文档移动到 iframe
【发布时间】:2016-06-14 00:15:56
【问题描述】:

我正在向我们的几个网站添加聊天功能。聊天会将用户与我们帮助台的人联系起来,以帮助他们使用网站。我们的帮助台人员希望聊天窗口看起来像页面侧面的标签并滑出,而不是在新窗口中弹出。但是,我想让用户在不丢失聊天记录的情况下浏览网站。

为此,我一直在尝试在聊天开始后将整个页面移动到 iframe 中(聊天在 iframe 之外),以便用户可以在 iframe 内浏览网站而不会丢失聊天。

我使用 this answer 开始使用,这在视觉上效果很好。但是,后台的一些 javascript 会中断。

其中一个站点是 ASP.NET Web 表单。另一个是MVC。我一直在使用网络表单。一旦页面被移动到 iframe 中,调用 __doPostBack 之类的东西就会中断,因为 javascript 上下文被抛在后面。

一旦用户点击一个链接(一个真正的链接,而不是 __doPostBack)并且 iframe 刷新,那么一切都会完美运行。

我怎么看,我有几个选择:

  1. 以某种方式将所有 javascript 变量从 window.top 复制到 iframe 中。希望不必知道所有变量名。我尝试了this.contentWindow.__doPostBack = window.top.__doPostBack,它有效,但缺少其他变量,因此最终失败:
  2. 以某种方式切换 iframe 的上下文以查看顶部窗口上下文,如果这可能的话?可能不会。
  3. 另一个想法是不要立即将页面移动到 iframe 中,而是等到页面更改后再将新页面加载到新的 iframe 中。但我不知道如何参与该事件并劫持它。
  4. 还有别的吗?

这些网站仅供我们员工使用,所以我只需要支持 IE11 和 Chrome。

更新:

感谢 LGSon 让我走上了使用目标属性的轨道(所以我可以使用方法 #3)。下面是我最终做的事情。当我弹出聊天时,我会拨打loadNextPageInIframe()。我在这里使用 jQuery,因为我们已经在我们的网站上使用了它,但是没有任何事情都可以完成。我在所有没有指向另一个框架或_blank的目标的链接上设置目标。我把 _parent 排除在外,但我认为我们无论如何都不会使用它。

我在一个名为“chatwindow”的全局变量中引用了我的聊天窗口 div。

在某些情况下这仍然可能不起作用,例如如果有一些 javascript 直接设置 window.location。如果我们的站点中有任何这样做的东西,我将不得不添加一种方法来处理它。

function loadNextPageInIframe() {
    var frame = $("<iframe id=\"mainframe\" name=\"mainframe\" />").css({
        position: "fixed",
        top: 0,
        left: 0,
        width: "100%",
        height: "100%",
        border: "none",
        display: "none"
    }).appendTo("body");

    $("form, a:not([target]), a[target=''], a[target='_top'], a[target='_self']").attr("target", "mainframe");

    var firstload = true;
    frame.load(function () {
        //Runs every time a new page in the iframe loads
        if (firstload) {
            $(frame).show();
            //Remove all elements from the top window except the iframe and chat window
            $("body").children().not(frame).not(window.top.chatwindow).remove();
            firstload = false;
        }
        //Make the browser URL and title reflect the iframe every time it loads a new page
        if (this.contentWindow && this.contentWindow.location.href && window.top.location.hostname === this.contentWindow.location.hostname && window.top.location.href !== this.contentWindow.location.href) {
            var title = this.contentDocument.title;
            document.title = title;
            if (window.top.history.replaceState) window.top.history.replaceState(null, title, this.contentWindow.location.href);
        }
    });
}

【问题讨论】:

    标签: javascript asp.net iframe


    【解决方案1】:

    我建议您执行以下操作

    1. 获取所有链接
    2. 附加一个事件单击处理程序以在有人单击链接时进行拦截
    3. 在点击事件时,检查聊天是否正在进行,如果是,则使用新链接提供 iframe
    var links = querySelectorAll("a");
    
    for (var i = 0; i < links.length; i++) {
    
      links[i].addEventListener('click', function(e) {
    
        if (isChatInProgress()) {
    
          e.preventDefault();  //stop the default action
    
          document.getElementById("your_iframe_id").src = e.target.href;
    
          // anything else here, like toggle tabs etc
    
        }
      });    
    }
    

    更新

    目前我看到了 4 种方式来处理表单

    1) 向表单添加 onsubmit 处理程序

    function formIsSubmitted(frm) {
        if (isChatInProgress()) {
            frm.target = "the iframe";
        }
        return true;
    }
    
    <form id="form1" runat="server" onsubmit="return formIsSubmitted(this)">
    

    2) 为您的按钮添加点击处理程序

    function btnClick(btn) {
        if (isChatInProgress()) {
            btn.form.target = "the iframe";
        }
        return true;
    }
    
    <asp:Button runat="server" ID="ButtonID" Text="ButtonText" 
        OnClick="Button_Click" OnClientClick="return btnClick(this);" />
    

    3) 聊天开始时,您遍历每个表单并更改其目标属性

    function startChat() {
    
      var forms = querySelectorAll("form");
    
      for (var i = 0; i < forms.length; i++) {
    
        forms[i].target = "the iframe";
    
      });    
    }
    

    4) 覆盖原来的回发事件(Src:intercept-postback-event

     // get reference to original postback method before we override it  
     var __doPostBackOriginal = __doPostBack;  
    
     // override  
     __doPostBack = function (eventTarget, eventArgument) {
    
        if (isChatInProgress()) {
            theForm.target = "the iframe";
        }
    
       // call original postback  
       __doPostBackOriginal.call(this, eventTarget, eventArgument);  
     }  
    

    更新 2

    解决这个问题的绝对最佳方法当然是使用 AJAX 来加载页面和聊天内容,但如果您的网站尚未使用它,这可能意味着更大的工作量。

    如果您不想这样做,您仍然可以使用 AJAX 进行聊天,如果用户要导航到新页面,聊天应用程序会重新创建正在进行的聊天窗口及其所有内容。

    【讨论】:

    • 问题是不仅链接可以移动到另一个页面。任何 ASP.NET 按钮都会在 javascript 中调用 __doPostBack,最终触发 HTTP POST。
    • @GabrielLuci 更新了我的答案。会考虑一下,看看我是否也能找到其他选择。
    • @GabrielLuci 为您添加了更多选项。
    • @GabrielLuci 为您添加了另一个选项。
    • re:您的第二次更新:使事情复杂化的另一个问题是聊天界面(“他说她说”位)由供应商处理。所以这也在 iframe 中。如果我可以完全控制一切,那么是的,我会在服务器端保持聊天状态并在每次页面加载时重建聊天窗口。
    【解决方案2】:

    我建议不要在 iframe 中加载内容 - 将聊天构建为 iframe,并在页面上使用 jQuery 模式弹出窗口进行聊天。

    您可以将 jquery modal 固定到固定位置,默认情况下启用页面滚动。您需要相应地修改 css 以使弹出窗口保持在同一位置。

    如果您按照当前的方式行事 - 您将需要非常担心内容如何移动到 iframe,并且可能难以根据内容在不同页面上重复使用聊天。例如,假设您在页面上播放视频并且用户点击聊天 - 如果您将内容加载到 iframe - 用户将失去他查看的距离等状态。

    【讨论】:

    • 请问这个解决方案是如何让用户在聊天时浏览网站的? ...当有人转到另一个页面时聊天会发生什么,或者通过模态聊天弹出窗口你的意思是他们不能?
    • 正如 LGSon 所提到的,这并不能解决在页面导航中保持聊天窗口打开的需要。如果我想这样做,我看不到使用 iframe 的方法。
    • 我最初以为您想在页面中导航 - 也就是说,滚动页面但保持聊天窗口打开且可见。如果您想单击链接并仍然打开聊天窗口 - 那么,仍然将聊天窗口作为 iframe。根据您的需要将聊天数据存储在数据库或会话中。创建一个关于是否显示或隐藏聊天窗口的逻辑(可能是存储在会话中的布尔变量)。当点击链接时 - 使用逻辑显示/隐藏聊天框并显示数据库或会话中的内容。
    【解决方案3】:

    在我看来,将整个网站添加为“I-Frame”不是一个好的设计实践,也不是解决问题的好方法。我的建议是:

    1. 确保在您网站的所有页面中都加载了“聊天”应用程序
    2. 每当“聊天”启动时,要么建立“网络套接字”连接,要么以某种方式维护服务器上的状态
    3. 将“聊天”配置为“最小化”、“打开”等,并将它们存储在您的 cookie 或会话存储中
    4. 在每次页面加载时,也调用“聊天”应用程序。从 sessionStorage 或 cookie 中读取 Chat 相关配置,并将其状态保持为“最小化”或“打开”等,包括 X 和 Y 位置,如果您想将其设为“浮动”
    5. 每次,要么通过 Ajax 从服务器获取整个对话,要么尝试从“本地存储”存储和获取,并且只为来自对方的任何更新执行 Ajax
    6. 使用基于 CSS 的“浮动”相关属性使其浮动并位于某侧。

    这将确保您的聊天可供用户使用,但他可以浏览整个网站。

    【讨论】:

    • 不幸的是,我无法控制实际的聊天界面(“他说她说”位)这一事实使我受到限制。该部分托管在另一台服务器上并由供应商开发。所以我必须在 iframe 中拥有聊天界面,这意味着我可以让 iframe 在页面之间保持活跃的唯一方法是将主页面也放在 iframe 中。
    • 嗨@GabrielLuci,我可以知道您使用的供应商吗?如果它是任何标准的聊天服务提供商,并且如果他提供了一些不错的 API,那么大多数时候,他们应该能够为每个聊天会话生成唯一的 ID。如果您可以获取该 ChatSessionID,那么您需要做的就是在每个页面上,您只需使用该 ChatSession ID 调用 API,其余的事情将自行处理。
    • 我们使用Computer Talk的ICE平台。我们的帮助台已经将它用于他们的电话队列。到目前为止,大部分的交流都是与项目经理类型的人进行的,因此很难准确判断什么是可能的。我最近刚刚与他们的开发人员取得了联系,所以一旦我们开始谈论更多事情,情况可能会有所改变。
    猜你喜欢
    • 1970-01-01
    • 2014-07-07
    • 2018-07-29
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 2016-11-07
    • 2020-02-07
    相关资源
    最近更新 更多