Javascript 小部件如何在没有 iFrame 的情况下制作?

How are Javascript widgets made without iFrames?

我有一个聊天小部件,我想将它嵌入其他人的网站。它看起来就像 Intercom 和所有其他聊天弹出窗口。无论您滚动到哪里,我都想让聊天弹出窗口固定在屏幕的右下角。但是,当我将聊天应用程序作为 iframe 导入并给它 position: fixed; bottom: 0px; right: 15px; 时,iframe 没有到达我期望的位置。

我意识到 iframe 不是嵌入式 JS 小部件的最佳选择,所有最好的嵌入式应用程序都从文件存储中导入 .js 文件。在网上搜索了几个小时后,我还没有找到关于如何制作挂接到 a 并呈现小部件的 JS 文件的 explanation/tutorial。您是如何制作其中一个纯 javascript 应用程序的,它们叫什么? (我假设不是网络组件,因为小部件已经存在很长时间了)。

抱歉,如果这个问题有点菜鸟。在我尝试自己实施之前,我从来不知道这是一回事。谁能指出我如何开始制作 JS 网络小部件的正确方向?谢谢! (也许 ReactJS 到 VanillaJS 转换器会非常酷)

纯 Javascript 应用程序称为 SPA - 单页应用程序 - 他们可以完全控制文档(页面)。但是既然你问的是嵌入一个小部件,我不认为这是这个问题的问题(有大量的信息。在 SPA 的网络上)。

我打算建议您今后使用 Web 组件来执行此操作 - 现在有可用的 polyfill 可以在几乎所有浏览器上运行 - 但由于您的问题提到您想知道没有它是如何完成的,我在下面详细介绍了我的一种方法。

当创建一个纯 JS 小部件时,您需要确保您知道 a) 您无法控制全局 space 和 b) 它需要与其余部分一起使用页。此外,由于您没有使用 Web 组件(并且正在寻找纯 javascript(无库)),因此您还必须初始化小部件 "manually",然后将其插入到所需的页面location - 与声明式方法相反,在这种方法中,您为小部件分配了一个 HTML 标签名称,您只需将其添加到文档中,魔法就会发生 :)

让我这样分解:

小部件工厂

这是一个简单的 Javascript 小部件工厂 - create() returns 一个 HTML 元素与您的小部件:

const Widget = Object.create({
    create(chatId) {
        const wdg = document.createElement("div")
        wdg.classList.add("chat-box");
        wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
        // Load your chat data into UI
        return wdg;
    }
});

要使用上面的方法创建一个新的小部件(HTML 元素),您将:

const myWidgetInstance = Widget.create("chat-12345");

并将此小部件插入页面的给定位置(例如,在 ID 为 "chat_box" 的 DIV 元素内,您将:

document.getElementById("chat_box").appendChild(myWidgetInstance);

这就是使用本机(网络)平台创建 Widget 的基础知识:)

正在创建一个 reusable/embeddable 组件

交付可重用和可嵌入组件时的一个关键目标是确保您不依赖全局 space。因此,您的交付方法(更像是您的构建过程)会将所有内容打包在一个 JavaScript IIFD 中,这也会为您的所有代码创建一个私有范围。

这些类型的单例 reusable/embeddable 组件的另一个重要方面是您的元素样式需要确保它们不会 "leak" 出来并影响页面的其余部分(需要和别人玩得开心)。我不会在这里详细介绍这个领域。 (仅供参考:这也是 Web 组件真正派上用场的领域)

这是一个聊天组件示例,您可以将其添加到页面中您希望它出现的任何位置。该组件作为 <script> 标签交付,其中包含所有代码:

<script>(function() {
    const Widget = Object.create({
        create(chatId) {
            const wdg = document.createElement("div");
            wdg.classList.add("chat-box");
            wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
            // Load your chat data into UI
            return wdg;
        }
    });

    const myWidgetInstance = Widget.create("chat-12345");
    const id = `chat-${ Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) }`;
    document.write(`<div id="${ id }"></div>`);
    document.getElementById(id).appendChild(myWidgetInstance);
})();</script>

所以你可以在多个地方使用它,只需将这个脚本标签放在所需的位置:

<body>
    <div>
        <h1>Chat 1</h1>
        <script>/* script tag show above */</script>
    </div>
    ...
    <div>
        <h1>Chat 2</h1>
        <script>/* script tag show above */</script>
    </div>
</body>

这只是如何完成的示例方法。您将不得不添加更多内容以支持将选项传递给每个小部件(例如聊天 ID)、定义样式以及其他可能提高运行时效率的改进。

另一种方法

您可以添加 "script" 一次并等待页面的其余部分加载,然后在文档中搜索 "known" 组元素(例如任何具有 CSS Class of chat-box),然后在其中初始化一个小部件(jQuery 使这种方法流行起来)。

示例: 请注意如何在 DOM 元素中使用 data 属性来存储更多特定于您的小部件的数据。

<div class="chat-box" data-chatid="123"></div>

<script>(function() {
    const Widget = Object.create({
        create(chatId) {
            const wdg = document.createElement("div");
            wdg.classList.add("chat-box");
            wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
            // Load your chat data into UI
            return wdg;
        }
    });
    const initWhenReady = () => {
        removeEventListener("DOMContentLoaded", initWhenReady);
        Array.prototype.forEach.call(document.querySelectorAll(".chat-box"), ele => {
            const myWidgetInstance = Widget.create(ele.dataset.chatid);
            ele.appendChild(myWidgetInstance);
        });
    };
    addEventListener('DOMContentLoaded', initWhenReady);
})();</script>

希望这对您有所帮助。

在没有 third-party 库的情况下创建 Javascript 小部件的最佳方法是创建自定义元素。

下面link:Custom Elements v1很好的介绍了这项技术

请参阅下面的最小示例:

class Chat extends HTMLElement {
    connectedCallback () {
        this.innerHTML = "<textarea>Hello</textarea>"    
    }
}

customElements.define( "chat-widget", Chat )
<chat-widget>
</chat-widget>