目錄
阻止預設行為
獲得拖曳的檔案
首頁 web前端 js教程 js控製檔拖曳並取得拖曳內容實作程式碼

js控製檔拖曳並取得拖曳內容實作程式碼

Feb 22, 2018 am 09:03 AM
javascript 內容 實現

js能夠監聽到拖曳的事件有drag、dragend、dragenter、dragexit(沒有瀏覽器實作)、dragleave、dragover、dragstart、drop,詳細的內容可以看MDN。其中,與拖曳檔案相關的事件有dragenter(檔案拖曳)、dragover(檔案拖曳在懸浮)、dragleave(檔案拖曳離開)、drop(檔案拖曳放下)。

拖曳事件可以綁定到指定的DOM元素上,可以綁定到整個頁面中。


var dropEle = document.querySelector('#dropZone');
dropEle.addEventListener('drop', function (e) {
  // 
}, false);

document.addEventListener('drop', function (e) {
  // 
}, false);
登入後複製

阻止預設行為

一般來說,我們只需要把處理拖曳檔案的業務邏輯寫到drop事件中就可以了,為什麼還要綁定dragenter、dragover、dragleave這三個事件呢?

因為當你拖曳一個文件到沒有對拖曳事件進行處理的瀏覽器中的時候,瀏覽器會打開這個文件,例如拖曳一張圖片瀏覽器會打開這個圖片,在沒有PDF閱讀器的時候也可以拖曳一個PDF到瀏覽器中,瀏覽器就會開啟這個PDF檔案。

如果瀏覽器打開了拖曳的文件,頁面就跳走了,我們希望得到拖曳的文件,而不是讓頁面跳走。上面說到瀏覽器會開啟拖曳的檔案是瀏覽器的預設行為,我們需要阻止這個預設行為,就需要再上述的事件中進行阻止。


dropZone.addEventListener("dragenter", function (e) {
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("dragover", function (e) {
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("dragleave", function (e) {
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();
  // 处理拖拽文件的逻辑
}
登入後複製

實際上dragenter不阻止預設行為也不會觸發瀏覽器開啟文件,為了防止某些瀏覽器可能有的兼容性問題,把拖曳週期中的所有的事件都阻止預設行為並且阻止了事件冒泡。

獲得拖曳的檔案

我們會在drop這個事件的回呼中的事件物件能夠得到檔案物件。

在事件物件中,一個e.dataTransfer這樣的屬性,它是一個DataTransfer類型的數據,有如下的屬性

屬性 類型說明
dropEffectString用來hack某些相容性問題
effectAllowedString#暫時不用
filesFileList拖曳的檔案清單
itemsDataTransferItemList#拖曳的資料(有可能是字串)
typesArray拖曳的資料類型該屬性在Safari下比較混亂

在Chrome中我们用items对象获得文件,其他浏览器用files获得文件,主要是为了处理拖拽文件夹的问题,最好不允许用户拖拽文件夹,因为文件夹内可能还有文件夹,递归上传文件会很久,如果不递归查找,只上传目录第一层级的文件,用户可能以为上传功能了,但是没有上传子目录文件,所以还是禁止上传文件夹比较好,后面我会说要怎么处理。

Chrome获取文件


dropZone.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();
  
  var df = e.dataTransfer;
  var dropFiles = []; // 存放拖拽的文件对象
  
  if(df.items !== undefined) {
    // Chrome有items属性,对Chrome的单独处理
    for(var i = 0; i < df.items.length; i++) {
      var item = df.items[i];
      // 用webkitGetAsEntry禁止上传目录
      if(item.kind === "file" && item.webkitGetAsEntry().isFile) {
        var file = item.getAsFile();
        dropFiles.push(file);
      }
    }
  }
}
登入後複製

其他浏览器获取文件

这里只测试了Safari,其他浏览器并没有测试,不过看完本文一定也有思路处理其他浏览器的兼容情况。


dropZone.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();
  
  var df = e.dataTransfer;
  var dropFiles = []; // 存放拖拽的文件对象
  
  if(df.items !== undefined) {
    // Chrome拖拽文件逻辑
  } else {
    for(var i = 0; i < df.files.length; i++) {
      dropFiles.push(df.files[i]);
    }
  }
}
登入後複製

由于Safari没有item,自然也没有webkitGetAsEntry,所以在Safari无法确定拖拽的是否是文件还是文件夹。

非Chrome内核浏览器判断目录的方法

浏览器获取到的每个file对象有四个属性:lastModified、name、size、type,其中type是文件的MIME Type,文件夹的type是空的,但是有些文件没有MIME Type,如果按照type是否为空判断是不是拖拽的文件夹的话,会误伤一部分文件,所以这个方法行。

那么还有什么方法可以判断呢,思路大概是这样子的,用户拖拽的文件和文件夹应该是不一样的东西,用File API操作的时候应该会有区别,比如进行某些操作的时候,文件就能够正常操作,但是文件夹就会报错,通过错误的捕获就能够判断是文件还是文件夹了,好我们根据这个思路来写一下。


dropZone.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();

  var df = e.dataTransfer;
  var dropFiles = [];
  
  if(df.items !== undefined){
    // Chrome拖拽文件逻辑
  } else {
    for(var i = 0; i < df.files.length; i++){
      var dropFile = df.files[i];
      if ( dropFile.type ) {
        // 如果type不是空串,一定是文件
        dropFiles.push(dropFile);
      } else {
        try {
          var fileReader = new FileReader();
          fileReader.readAsDataURL(dropFile.slice(0, 3));

          fileReader.addEventListener(&#39;load&#39;, function (e) {
            console.log(e, &#39;load&#39;);
            dropFiles.push(dropFile);
          }, false);

          fileReader.addEventListener(&#39;error&#39;, function (e) {
            console.log(e, &#39;error,不可以上传文件夹&#39;);
          }, false);

        } catch (e) {
          console.log(e, &#39;catch error,不可以上传文件夹&#39;);
        }
      }
    }
  }
}, false);
登入後複製

上面代码创建了一个FileReader实例,通过这个实例对文件进行读取,我测试读取一个1G多的文件要3S多,时间有点长,就用slice截取了前3个字符,为什么是前3个不是前2个或者前4个呢,因为代码是我写的,我开心这么写呗~

如果load事件触发了,就说明拖拽过来的东西是文件,如果error事件触发了,就说明是文件夹,为了防止其他可能的潜在错误,用try包起来这段代码。

三方应用的一点点小hack

经过测试发现通过Mac的Finder拖拽文件没有问题,但是有时候文件并不一定在Finder中,也可能在某些应用中,有一个应用叫做圈点,这个应用的用户反馈文件拖拽失效,去看了其他开源文件上传的源码,发现了这样一行代码:


dropZone.addEventListener("dragover", function (e) {
  e.dataTransfer.dropEffect = &#39;copy&#39;; // 兼容某些三方应用,如圈点
  e.preventDefault();
  e.stopPropagation();
}, false);
登入後複製

需要把dropEffect置为copy,上网搜了下这个问题,源码文档中也没有说为什么要加这个,有兴趣的同学可以找一下为什么。

可以拿来就用的代码

由于用了FileReader去读取文件,这是一个异步IO操作,为了记录当前处理了多少个文件,以及什么时候触发拖拽结束的回调,写了一个checkDropFinish的方法一直去比较处理的文件数量和文件总数,确定所有文件处理完了后就去调用完成的回调。

另外,我在最后调试异步处理的时候,用的断点调试,发现断点调试在Safari中会导致异步回调不触发,需要自己调试定制功能的同学注意下。


// 获得拖拽文件的回调函数
function getDropFileCallBack (dropFiles) {
  console.log(dropFiles, dropFiles.length);
}

var dropZone = document.querySelector("#dropZone");
dropZone.addEventListener("dragenter", function (e) {
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("dragover", function (e) {
  e.dataTransfer.dropEffect = &#39;copy&#39;; // 兼容某些三方应用,如圈点
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("dragleave", function (e) {
  e.preventDefault();
  e.stopPropagation();
}, false);

dropZone.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();

  var df = e.dataTransfer;
  var dropFiles = []; // 拖拽的文件,会放到这里
  var dealFileCnt = 0; // 读取文件是个异步的过程,需要记录处理了多少个文件了
  var allFileLen = df.files.length; // 所有的文件的数量,给非Chrome浏览器使用的变量

  // 检测是否已经把所有的文件都遍历过了
  function checkDropFinish () {
    if ( dealFileCnt === allFileLen-1 ) {
      getDropFileCallBack(dropFiles);
    }
    dealFileCnt++;
  }

  if(df.items !== undefined){
    // Chrome拖拽文件逻辑
    for(var i = 0; i < df.items.length; i++) {
      var item = df.items[i];
      if(item.kind === "file" && item.webkitGetAsEntry().isFile) {
        var file = item.getAsFile();
        dropFiles.push(file);
        console.log(file);
      }
    }
  } else {
    // 非Chrome拖拽文件逻辑
    for(var i = 0; i < allFileLen; i++) {
      var dropFile = df.files[i];
      if ( dropFile.type ) {
        dropFiles.push(dropFile);
        checkDropFinish();
      } else {
        try {
          var fileReader = new FileReader();
          fileReader.readAsDataURL(dropFile.slice(0, 3));

          fileReader.addEventListener('load', function (e) {
            console.log(e, 'load');
            dropFiles.push(dropFile);
            checkDropFinish();
          }, false);

          fileReader.addEventListener('error', function (e) {
            console.log(e, 'error,不可以上传文件夹');
            checkDropFinish();
          }, false);

        } catch (e) {
          console.log(e, 'catch error,不可以上传文件夹');
          checkDropFinish();
        }
      }
    }
  }
}, false);
登入後複製

相关推荐:

如何实现文件拖拽上传

html5文件拖拽上传的示例代码分享

Dropzone.js实现文件拖拽上传功能

以上是js控製檔拖曳並取得拖曳內容實作程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

華為手機如何實現雙微信登入? 華為手機如何實現雙微信登入? Mar 24, 2024 am 11:27 AM

華為手機如何實現雙微信登入?隨著社群媒體的興起,微信已成為人們日常生活中不可或缺的溝通工具之一。然而,許多人可能會遇到一個問題:在同一部手機上同時登入多個微信帳號。對於華為手機用戶來說,實現雙微信登入並不困難,本文將介紹華為手機如何實現雙微信登入的方法。首先,華為手機自帶的EMUI系統提供了一個很方便的功能-應用程式雙開。透過應用程式雙開功能,用戶可以在手機上同

Microsoft Edge瀏覽器開啟是360導航怎麼改-更改開啟是360導航的方法 Microsoft Edge瀏覽器開啟是360導航怎麼改-更改開啟是360導航的方法 Mar 04, 2024 pm 01:50 PM

怎麼更改MicrosoftEdge瀏覽器打開是360導航的頁面呢?其實很簡單,那麼現在小編就和大家一起分享關於更改MicrosoftEdge瀏覽器打開是360導航頁面的方法,有需要的朋友可以來看看哦,希望可以幫助大家。開啟MicrosoftEdge瀏覽器。我們看到是下圖這種頁面。點擊右上角的三點圖示。點選“設定”。在設定頁面的左側欄裡點選「啟動時」。點選右側欄裡的圖中示意的三點(不要能點選「開啟新標籤頁」),然後點選編輯,將網址改成「0」(或其他無意義的數字)。然後點選「儲存」。接下來,選擇“

PHP程式設計指南:實作斐波那契數列的方法 PHP程式設計指南:實作斐波那契數列的方法 Mar 20, 2024 pm 04:54 PM

程式語言PHP是一種用於Web開發的強大工具,能夠支援多種不同的程式設計邏輯和演算法。其中,實作斐波那契數列是一個常見且經典的程式設計問題。在這篇文章中,將介紹如何使用PHP程式語言來實作斐波那契數列的方法,並附上具體的程式碼範例。斐波那契數列是一個數學上的序列,其定義如下:數列的第一個和第二個元素為1,從第三個元素開始,每個元素的值等於前兩個元素的和。數列的前幾元

如何在華為手機上實現微信分身功能 如何在華為手機上實現微信分身功能 Mar 24, 2024 pm 06:03 PM

如何在華為手機上實現微信分身功能隨著社群軟體的普及和人們對隱私安全的日益重視,微信分身功能逐漸成為人們關注的焦點。微信分身功能可以幫助使用者在同一台手機上同時登入多個微信帳號,方便管理和使用。在華為手機上實現微信分身功能並不困難,只需要按照以下步驟操作即可。第一步:確保手機系統版本和微信版本符合要求首先,確保你的華為手機系統版本已更新至最新版本,以及微信App

Cheat Engine如何設定中文? Cheat Engine設定中文方法 Cheat Engine如何設定中文? Cheat Engine設定中文方法 Mar 13, 2024 pm 04:49 PM

  CheatEngine是一款遊戲編輯器,能夠對遊戲的記憶體進行編輯修改。但是它的預設語言是非中文的,對於很多小夥伴來說比較不方便,那麼CheatEngine怎麼設定中文呢?今天小編就跟大家詳細介紹一下CheatEngine設定中文的方法,希望可以幫助到你。  設定方法一  1、雙擊開啟軟體,點選左上角的「edit」。  2、接著點選下方選項清單中的「settings」。  3、在開啟的視窗介面中,點選左側欄中的“languages”

掌握Golang如何實現遊戲開發的可能性 掌握Golang如何實現遊戲開發的可能性 Mar 16, 2024 pm 12:57 PM

在現今的軟體開發領域中,Golang(Go語言)作為一種高效、簡潔、並發性強的程式語言,越來越受到開發者的青睞。其豐富的標準庫和高效的並發特性使它成為遊戲開發領域的一個備受關注的選擇。本文將探討如何利用Golang來實現遊戲開發,並透過具體的程式碼範例來展示其強大的可能性。 1.Golang在遊戲開發中的優勢作為靜態類型語言,Golang正在建構大型遊戲系統

PHP遊戲需求實作指南 PHP遊戲需求實作指南 Mar 11, 2024 am 08:45 AM

PHP遊戲需求實現指南隨著網路的普及和發展,網頁遊戲的市場也越來越火爆。許多開發者希望利用PHP語言來開發自己的網頁遊戲,而實現遊戲需求是其中一個關鍵步驟。本文將介紹如何利用PHP語言來實現常見的遊戲需求,並提供具體的程式碼範例。 1.創造遊戲角色在網頁遊戲中,遊戲角色是非常重要的元素。我們需要定義遊戲角色的屬性,例如姓名、等級、經驗值等,並提供方法來操作這些

Microsoft Edge在哪設定顯示下載按鈕-Microsoft Edge設定顯示下載按鈕的方法 Microsoft Edge在哪設定顯示下載按鈕-Microsoft Edge設定顯示下載按鈕的方法 Mar 06, 2024 am 11:49 AM

大家知道MicrosoftEdge在哪設定顯示下載按鈕嗎?下文小編就帶來了MicrosoftEdge設定顯示下載按鈕的方法,希望對大家能夠有幫助,一起跟著小編來學習吧!第一步:先開啟MicrosoftEdge瀏覽器,點選右上角【...】標識,如下圖所示。步驟二:然後在彈出式選單中,點選【設定】,如下圖所示。第三步:接著點選介面左側【外觀】,如下圖所示。第四步:最後點選【顯示下載按鈕】右側按鈕,由灰變藍即可,如下圖所示。上面就是小編為大家帶來的MicrosoftEdge在哪設定顯示下載按鈕的

See all articles