在不受Host Page CSS影响的跨域主机页面中创建嵌入式JavaScript?

时间:2016-01-04 20:51:56

标签: javascript html css iframe gwt

可嵌入网站的大多数javascript小部件使用以下结构。首先你嵌入一个像这样的代码:

<script type="text/javascript">
window.$zopim||(function(d,s){var z=$zopim=function(c){
z._.push(c)},
$=z.s=d.createElement(s),
e=d.getElementsByTagName(s)[0];
z.set=function(o){
    z.set._.push(o)
};
z._=[];
z.set._=[];
$.async=!0;
$.setAttribute('charset','utf-8');
$.src='//v2.zopim.com/?2342323423434234234';
z.t=+new Date;
$.type='text/javascript';
e.parentNode.insertBefore($,e)})(document,'script');
</script>

然后,在加载页面时,此脚本会创建一个这样的html结构:

<div class="widget-class">
  <iframe src="about:blank">
    // the content of the widget
  </iframe>
</div

我在许多聊天服务中看到了相同的结构,例如:

https://en.zopim.com/ 
http://banckle.com/
https://www.livechatinc.com/

所有人都有共同点,他们的iframe没有src,即附加了一个网址。

更新:这是我用来将我的小部件代码加载到第三方网站的脚本:

<script type="text/javascript">
(function(d){
    var f = d.getElementsByTagName('SCRIPT')[0], p = d.createElement('SCRIPT');
    window.WidgetId = "1234";   
    p.type = 'text/javascript';
    p.setAttribute('charset','utf-8');
    p.async = true;     
    p.src = "//www.example.com/assets/clientwidget/chatwidget.nocache.js";     
    f.parentNode.insertBefore(p, f);
}(document));
</script>    

我希望集成GWT小部件的站点的CSS不应该影响GWT小部件的CSS。我将阻止主页的CSS影响我的GWT小部件的CSS。

注意:我也想从我的GWT小工具访问主机网站。
主页的域名是www.example.com,iframe的域名是www.widget.com。我还想从iframe设置主机域的cookie。

构建在这样的结构上运行的小部件的过程是什么?如何设置iframe的内容?那有什么模式吗?我怎么能用GWT

来做到这一点

4 个答案:

答案 0 :(得分:4)

我不了解GWT,但您可以通过简单的JavaScript轻松实现这一目标。

假设您正在创建在线计数小部件。首先,创建一个iframe:

<script id="your-widget">
  // Select the script tag used to load the widget.
  var scriptElement = document.querySelector("your-widget");
  // Create an iframe.
  var iframe = document.createElement("iframe");
  // Insert iframe before script's next sibling, i.e. after the script.
  scriptElement.parentNode.insertBefore(iframe, scriptElement.nextSibling);
  // rest of the code
</script>

然后使用JSONP获取在线计数(请参阅What is JSONP all about?),例如:

// The URL of your API, without JSONP callback parameter.
var url = "your-api-url";
// Callback function used for JSONP.
// Executed as soon as server response is received.
function callback(count) {
  // rest of code
}
// Create a script.
var script = document.createElement("script");
// Set script's src attribute to API URL + JSONP callback parameter.
// It makes browser send HTTP request to the API.
script.src = url + "?callback=callback";

然后处理服务器响应(在callback()函数内):

// Create a div element
var div = document.createElement("div");
// Insert online count to this element.
// I assume that server response is plain-text number, for example 5.
div.innerHTML = count;
// Append div to iframe's body.
iframe.contentWindow.document.body.appendChild(div);

这就是全部。您的整个代码可能如下所示:

要插入第三方网站的代码段:

<script type="text/javascript">
(function(d){
    var f = d.getElementsByTagName('SCRIPT')[0], p = d.createElement('SCRIPT');
    window.WidgetId = "1234";   
    p.type = 'text/javascript';
    p.setAttribute('charset','utf-8');
    p.async = true;
    p.id = "your-widget";
    p.src = "//www.example.com/assets/clientwidget/chatwidget.nocache.js";     
    f.parentNode.insertBefore(p, f);
}(document));
</script>    

服务器上的JavaScript文件:

// Select the script tag used to load the widget.
var scriptElement = document.querySelector("#your-widget");
// Create an iframe.
var iframe = document.createElement("iframe");
// Insert iframe before script's next sibling, i.e. after the script.
scriptElement.parentNode.insertBefore(iframe, scriptElement.nextSibling);

// The URL of your API, without JSONP callback parameter.
var url = "your-api-url";
// Callback function used for JSONP.
// Executed as soon as server response is received.
function callback(count) {
  // Create a div element
  var div = document.createElement("div");
  // Insert online count to this element.
  // I assume that server response is plain-text number, for example 5.
  div.innerHTML = count;
  // Append div to iframe's body.
  iframe.contentWindow.document.body.appendChild(div);
}
// Create a script.
var script = document.createElement("script");
// Set script's src attribute to API URL + JSONP callback parameter.
// It makes browser send HTTP request to the API.
script.src = url + "?callback=callback";

答案 1 :(得分:1)

修改
如果你希望你的小部件不受“外部”的任何css的影响,你必须加载到iframe。

添加到您的网站以加载任何gwt项目/小部件的代码:

<iframe id="1234" src="//www.example.com/assets/Chatwidget.html" style="border: 1px solid black;" tabindex="-1"></iframe>

注意:我没有加载nocache.js,而是加载了yourwidget.html文件。 像这样你的所有条款都不会受到来自外界的任何阶级的影响。

要访问此iframe之外的任何内容,您可以使用jsni methods。这仅适用于iframe和第三方的域名相同的情况。否则你可以使用window.postMessage

public native static void yourMethod() /*-{
     $wnd.parent.someMethodFromOutsideTheIframe(); 
}-*/;

<强> EDIT2:

通过使用上面的代码段,您可以确保您的小部件不会受到主页中任何css的影响。 要从小部件内部获取主页网址,只需添加此功能:

private native static String getHostPageUrl() /*-{
    return $wnd.parent.location.hostname;
}-*/;

<强> EDIT3:

因为您在2个不同的域上,所以必须使用window.postMessage。 这里有一个小例子可以帮助你:

除了iframe之外,您还必须在example.com的窗口中添加一个事件侦听器,以侦听来自iframe的消息。您还可以检查消息是否来自正确的来源。

<script>
    // Create IE + others compatible event handler
    var eventMethod = window.addEventListener ? "addEventListener"
            : "attachEvent";
    var eventer = window[eventMethod];
    var messageEvent = eventMethod == "attachEvent" ? "onmessage"
            : "message";

    // Listen to message from child window
    eventer(messageEvent, function(e) {
        //check for the correct origin, if wanted
        //if ( e.origin !== "http://www.widget.com" )
        //        return
        console.log('parent received message!:  ', e.data);
        //here you can set your cookie
        document.cookie = 'cookie=widget; expires=Fri, 1 Feb 2016 18:00:00 UTC; path=/'
    }, false);
</script>

从小部件内部调用此方法:

public native static void postMessageToParent(String message) /*-{
    //message to sent, the host that is supposed to receive it
    $wnd.parent.postMessage(message, "http://www.example.com");
}-*/;

我在pastebin上放了一个工作示例:

要插入页面的JavaScript:http://pastebin.com/Y0iDTntw
带有onmoduleload的gwt类:http://pastebin.com/QjDRuPmg

答案 2 :(得分:0)

这是我在cloud9(在线IDE)中使用javascript编写的全功能简单小部件示例项目,如果您想编辑它,请随时请求访问,查看是公开的(对于注册用户 - 注册是免费)。

来源: https://ide.c9.io/nmlc/widget-example, 结果: https://widget-example-nmlc.c9users.io/index.html

关于他们如何做的问题:

似乎zopim在客户端逐步构建其小部件,定义并要求基本模块(如__$$__meshim_widget_components_mobileChatWindow_MainScreen),这些模块由子模块组成,然后使用__$$__jx_ui_HTMLElement构建器处理所有内容,从而创建HTML元素并将它们附加到提供的父节点。所有这些都编译成聊天框的结果HTML。顺便说一下,从一些组件的名称来看,似乎他们用一些&#34; meshim&#34;来构建他们的小部件。图书馆,但我从来没有听说过这个图书馆。

this.dom.src='about:blank'
this.appendToParent(!0)
var H=this.iwin=this.dom.contentWindow
var I=this.idoc=r.extend(H.document)
I.write(G)
I.close()

我想,这是zopim服务为其小部件创建iframe的地方。我不确定为什么他们使用document.write而不是appendChild(document.write删除事件绑定),但我已经实现了两个版本 - 它们几乎相同,除了setIframeContents和{{1}函数。

希望有人会发现这有用:)。

答案 3 :(得分:0)

1)将内容加载到iframe的方法有很多种。 iframe有孤立的内容。你放在主机页面的iframe,没有src,因为浏览器安全政策,你不能简单地从其他域加载内容。但是你可以从其他域加载js。对于这个porpuse你需要使用JSONP

2)与主机页面和小部件iframe共享cookie,你需要像post

一样使用postMessage api