Bootstrap 4上傳檔案介面無法顯示檔案路徑或檔名

前幾天心血來潮用了Bootstrap 4提供的檔案選擇器,作為上傳檔案的用途,Bootstrap稱為「Custom file input」,可以覆寫原本HTML那個很醜的檔案上傳介面,但是照著說明書一套用才發現原本會顯示檔案路徑、檔名的功能竟然不見了?但是官網上面的範例是可以的啊?

Bootstrap 4 - Custom file input

在這個Custom file input官方的範例裡面,你隨便挑選一個電腦的檔案後,確實可以把檔案的檔名顯示在上面。

但是你自己依照寫法照抄一段到自己的網站,就不是這麼一回事了,以下方程式碼為例,是可以正確的彈出檔案對話方框,但是當你挑選完檔案後回到網站畫面,你會看到檔案挑選器原本要顯示路徑、檔名的地方,就是一片空白...WTF!

<div class="input-group">
  <div class="custom-file">
    <input type="file" class="custom-file-input" id="uUploadFile">
    <label class="custom-file-label" for="uUploadFile" aria-describedby="uUploadButton">Choose file</label>
  </div>
  <div class="input-group-append">
    <button class="btn btn-primary" type="button" id="uUploadButton">Upload</button>
  </div>
</div>

解決方法

經過翻找官網底層的HTML與JS,發現這個網頁背景有引入一個可疑的「https://getbootstrap.com/docs/4.3/assets/js/docs.min.js」,再進去裡面挖出下面這一段程式碼,原來Bootstrap有再背後自己做手腳啊!

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.bsCustomFileInput=e()}(this,function(){"use strict";var f={CUSTOMFILE:'.custom-file input[type="file"]',CUSTOMFILELABEL:".custom-file-label",FORM:"form",INPUT:"input"},o=function(t){if(0<t.childNodes.length)for(var e=[].slice.call(t.childNodes),n=0;n<e.length;n++){var o=e[n];if(3!==o.nodeType)return o}return t},a=function(t){var e=t.bsCustomFileInput.defaultText,n=t.parentNode.querySelector(f.CUSTOMFILELABEL);n&&(o(n).innerHTML=e)},n=!!window.File,i=function(t){if(t.hasAttribute("multiple")&&n)return[].slice.call(t.files).map(function(t){return t.name}).join(", ");if(-1===t.value.indexOf("fakepath"))return t.value;var e=t.value.split("\\");return e[e.length-1]};function h(){var t=this.parentNode.querySelector(f.CUSTOMFILELABEL);if(t){var e=o(t),n=i(this);n.length?e.innerHTML=n:a(this)}}function d(){for(var t=[].slice.call(this.querySelectorAll(f.INPUT)).filter(function(t){return!!t.bsCustomFileInput}),e=0,n=t.length;e<n;e++)a(t[e])}var p="bsCustomFileInput",y="reset",m="change";return{init:function(t,e){void 0===t&&(t=f.CUSTOMFILE),void 0===e&&(e=f.FORM);for(var n,o,i,r=[].slice.call(document.querySelectorAll(t)),A=[].slice.call(document.querySelectorAll(e)),a=0,l=r.length;a<l;a++){var c=r[a];Object.defineProperty(c,p,{value:{defaultText:(n=c,o=void 0,void 0,o="",i=n.parentNode.querySelector(f.CUSTOMFILELABEL),i&&(o=i.innerHTML),o)},writable:!0}),c.addEventListener(m,h)}for(var s=0,u=A.length;s<u;s++)A[s].addEventListener(y,d),Object.defineProperty(A[s],p,{value:!0,writable:!0})},destroy:function(){for(var t=[].slice.call(document.querySelectorAll(f.FORM)).filter(function(t){return!!t.bsCustomFileInput}),e=[].slice.call(document.querySelectorAll(f.INPUT)).filter(function(t){return!!t.bsCustomFileInput}),n=0,o=e.length;n<o;n++){var i=e[n];a(i),i[p]=void 0,i.removeEventListener(m,h)}for(var r=0,A=t.length;r<A;r++)t[r].removeEventListener(y,d),t[r][p]=void 0}}});
bsCustomFileInput.init();

如果有興趣的人,其實引入上方那段官方的解法到自己的JS內就可以運作了,但其實我不追求這麼泛用做法與效能,所以自己用jQuery自幹一個比較快,這樣做的目的是為了讓我以後可以看得懂自己在寫什麼,好維護才是重點。

$(function(){
  $(".custom-file-input").change(function () {
    $(this).next(".custom-file-label").html($(this).val().split("\\").pop());
  });
});
Bootstrap4 BS4 FileUpload FilePath FileName Nothing Empty Null Blank