週末長知識: 空白 iframe 指南

你曾經在網頁上使用過空白的 iframe 嗎? 我想大部分的人應該都沒有用過,我也沒有。 難得上週有機會一窺空白 iframe 的秘密,就做為本週的長知識內容來分享一下吧!

為了行文的方便,本文接下來的「空白 iframe」皆用「小白」簡稱之。

小白的樣貌

最簡單建立一個小白的方法:

<!-- 不需要寫 src 屬性 -->
<iframe></iframe>

<!-- 堅持要寫的話也只有 about:blank 這個選擇 -->
<iframe src="about:blank"></iframe>

在 DevTools 裡觀察的話就是長這個樣子:

blank_iframe.png

可以看到瀏覽器還貼心的建立了諸如 <html>, <head>, <body> 等基本的網頁元素。

為小白新增內容

空白的iframe不是沒有HTML檔案嗎?要怎麼樣修改裡面的內容啊?

基本的概念就是先取得小白的 document,接著再利用 document.write() 或直接對 <body>, <head>appendChild;其實方法還算簡單,只需要寫好相應的 function,想要塞什麼東西進去都不成問題囉!

上面的範例內已經涵蓋了大部分的使用方式:

  • 插入 style 及其內容
  • 插入 link 來外連 CSS
  • 插入 script 及其內容
  • 插入 HTML 內容

核心的概念就是 轉成 string 再塞進去,我想剩下的插入外連 script 應該難不倒你吧!

小白的特性

其實小白的特性說起來有點像最近越來越夯的 Web Components 裡的 Shadow DOM,想了解一下 Shadow DOM 的人可以看看這篇 Shadow DOM 101

我認為小白與 Shadow DOM 相像的部分在於:

  1. 共用的 JavaScript context
  2. 裡外隔絕的 DOM

讓我們來分別來解釋一下。

共用的 JavaScript context

如果是平常的 iframe,裡面的 window 跟外面的是分開的;如果裡面或外面想要互相存取對方的 window 內容,必須是在裡外同網域的情況下才有可能(或著要想辦法用 postMessage 來溝通),下面的例子示範了一下 同/不同 網域下的情況。

上面同網域的例子(index.html & mine.html)裡,它們是這樣存取對方的 window 內容的:

// 由外往內
$('#mine')[0].contentWindow.callMe();

// 由內往外
window.parent.callMe();

然而在使用小白的情況下,裡外的界線變得十分曖昧,讓我們仔細回顧一下方才修改小白內容的範例裡,“Enable button functionality” 功能的寫法。

$('#enable-button-func').one('click', function () {
  var DATA = JSON.stringify({
    iframeId: iframeId
  });
  
  var CODE = '' + (function (data) {
    var $doc = $($('#' + data.iframeId)[0].contentDocument);
    
    $doc.find('button')
    .click(function () {
      alert('Hurrah!!!');
    })
    .text('Click me!')
    .addClass('btn-warning');
  }).toString();
  
  addScript(iframeId, '(' + CODE + ')(' + DATA + ')');
});

然後看看它擺到小白裡的模樣。

injected_script.png

有看出端倪了嗎? 小白內明明就沒有載入 jQuery,為什麼我們插入的程式碼還可以順利執行呢?

沒錯,因為裡外其實是同一個 JavaScript context,同樣的 window,同樣的 document。 這也就是為什麼我們明明已經在裡面執行了,想要選取小白本身的 document 卻還需要透過傳入 iframeId 的方式來一步步取得。

裡外隔絕的 DOM

經過上面的幾段說明,我想這邊就不言而喻了。

所謂「裡外隔絕的 DOM」指的是:

  • 外面的 CSS 無法套用到小白裡面的 HTML 元素上
  • 外面的 document.querySelector() 找不到小白裡面的 DOM 元素

正因為小白裡面的 document 跟外面不同,才會造成上述的現象;而小白內的 DOM 元素其實可以用 .contentDocument.querySelector() 來取得。

使用小白的真實案例

說真的,長這麼大還真的沒有看過真的有拿出來秀給人看的小白。以前比較常見到的用法可能是給小白一個 name,然後可以用背景的方式來送一些東西給 server,像是 Instantly Submit HTML Form Using IFrame and JavaScript 內所說的這樣。

有聽過 Evernote Clearly 嗎?

Evernote Clearly 裡面的簡化文章後的頁面整頁就是一個小白! 整頁都是手把手硬幹出來的… 真的是佩服得五體投地。

(我沒有仔細爬過產出那頁面的 code 啦.. 可能已經有寫出好用工具了也說不定(?))

後記

本來是打算上週末就要刊出本文,但實在是太沒時間啦~~ 希望本期的週末長知識有滋潤你的心,謝謝收看。

題外話,ParrotTalks抄筆記 終於支援在 Evernote Clearly 中使用啦!

clearly_support.gif

馬上打開 Evernote Clearly + ParrotTalks抄筆記 來享受零滯留感的閱讀體驗!