前端地雷[JS] — 在js內無法直接開啟 input[type=file]

施慶銓
7 min readJul 13, 2018

--

今天下午後端工程師跑來找我

後: "欸欸, 這個按鈕怎麼按下去都沒反應??"

我: "哪裡? 怎可能!?我看看 "

(一點, 畫面毫無反應…. 應該是要有上傳檔案的彈出視窗才對……囧

我: "好吧, 我看一看, 應該是之前有動到壞掉了....”

======工程師日常結束…. 開始拆彈======

回到座位上, 腦海中的回憶開始倒帶, 到底是動到哪些地方…

首先, 這個功能大致上是從一選單中選擇其中一個選項, 這個選項點擊後會出現上傳檔案的彈出視窗

上傳檔案的input元素是動態產生, 它包在一個function內, 選單內的選項被點擊的時候再產生

upload function的code大概是這樣:

單純建立一個input的元素, 然後直接呼叫

後來, 因為PM說在呼叫上傳檔案前, 需要做一些資料驗證, 驗證確定沒問題後才能呼叫這個function

加上驗證

加上驗證後的呼叫方式

在驗證的function中(valid function) 主要做兩件事情

  1. 先從server 取得資料
  2. 驗證

2.1 正確 => 開啟上傳視窗

2.2 失敗 => 彈出確認視窗, 按下’OK’ 再出現上傳視窗

==== Case 1 測試: 驗證失敗====

驗證失敗

成功地出現確認視窗,

按下 "OK", 成功出現上傳視窗

End

==== Case 2 測試: 驗證成功====

驗證成功

(無反應....

燈愣!

Why…. ?

這狀況太詭異 , 單單就只是加了一個驗證function, 就出問題!?

這種程式碼越少的bug越難解

首先 debug起手式, 直接開console, 看看程式執行是否有誤, input元素是否存在

結果: 沒有error, input 元素乖乖地已經append到body上

但執行還是沒反應

開大絕: 直接在console上呼叫, “document.querySelector(‘input’).click()”

出現了!? WTF? 這不科學阿

程式碼逐行檢視似乎也都正常, funtion都有呼叫到 就是沒反應…

沒辦法, 死馬當活馬醫, 只好關掉編輯器(誤), 是請教stackoverflow大神

====== 開始問神 ======

首先, 先找有關input file無法點擊的問題

找到一篇關於瀏覽器影響的可能性

1. For the security reasons Opera and Firefox don’t allow to trigger file input.

2. The only convenient alternative is to create a “hidden” file input (using opacity, not “hidden” or “display: none”!) and afterwards create the button “below” it. In this way the button is seen but on user click it actually activates the file input.

Button replaces input

試了其他瀏覽器, Firefox、IE , 居然正常, 只有Chrome會這樣, 所以是瀏覽器的問題.....嗎??

But, 在之前在Chrome執行也都沒問題啊… 這也算是找到另一個冷知識@@

事情還沒結束, 繼續找

過了不知多久.... 準備要放棄直接整個重寫時,

腦中浮現一個keyword, 找找”js input file callback"

賓果!

找到一篇關於在ajax callback內呼叫input file的問題

下方大大第一句就提到

Its not possible right now to open a file popup window from an async ajax callback due to w3c recommended security features in the browsers.

接著提到在 w3c.org 有規定在某些條件下才能出現popup視窗, 只要符合其中一條就允許出現:

An algorithm is allowed to show a popup if any of the following conditions is true:

1. The task in which the algorithm is running is currently processing an activation behavior whose click event was trusted.

2. The task in which the algorithm is running is currently running the event listener for a trusted event whose type is in the following list:

change, click, dblclick, mouseup, reset, submit

3. The task in which the algorithm is running was queued by an algorithm that was allowed to show a popup, and the chain of such algorithms started within a user-agent defined timeframe.

裡面提到的意思大概就是, 如果要能夠出現popup視窗, 不管你要觸發的元素可以直接讓使用者click或是需要透過監聽的方式綁定下方的行為來開啟popup視窗都可行 (只要是user 在畫面上直接觸發事件, event 的isTrusted屬性會是true)

change, click, dblclick, mouseup, reset, submit

但如果isTrusted是false就沒辦法開啟嗎?

最後一點提到: 你也可以在程式內觸發, but, 該程式被觸發的人必須是user, 也就是我程式問題的原因, 由ajax 的callback呼叫, 導致browser不承認 這個呼叫方法…

終於…. 折騰了半天… 才知道沒想到誤入一個不起眼的地雷

目前的我解決方法就是單純的把ajax呼叫的function獨立出來預先載入, 之後驗證的時候就直接處理

果然就這樣成功了...!

code其實沒有改很多, 但花了不少時間找炸彈….

不知道會不會也有很多人也會遇到這個問題, 如果有的話也希望可以幫到其他人解決

--

--