最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript實現多文件拖動上傳功能

 更新時間:2024年04月23日 10:17:46   作者:橙某人  
這篇文章主要為大家詳細介紹了如何使用JavaScript實現多文件拖動上傳功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

寫在開頭

哈嘍,各位好呀!

近來開始回暖,風和日麗,晴空萬里,連續(xù)幾天都是好天氣,好心情,真是一個很棒的季節(jié)呢。

本章要分享的內容如下,請按需食需:

大家對于文件上傳功能肯定不陌生了,通常我們會直接采用UI框架提供的現成上傳組件,因為從頭開始編寫一個上傳組件確實較為繁瑣。然而,這次小編將僅使用純 JS 來實現一個拖動上傳的功能。

拖動事件

而要完成拖動上傳功能,首先,我們要來談論的第一件事情就是其中的拖動事件。

瀏覽器總共有七個拖動相關的事件:dragdragenddragenter、dragleavedragover、dragstart、drop。

這里我們就不去細講每個事件了,你可以自行去MDN上查閱。傳送門

本次我們僅會用到如下四個事件:

  • dragenter:在可拖動的元素或者被選擇的文本進入一個有效的放置目標時觸發(fā)。
  • dragleave:在拖動的元素或選中的文本離開一個有效的放置目標時被觸發(fā)。
  • dragover:在可拖動的元素或者被選擇的文本被拖進一個有效的放置目標時(每幾百毫秒)觸發(fā)。
  • drop:在元素或文本選擇被放置到有效的放置目標上時觸發(fā)。為確保 drop 事件始終按預期觸發(fā),應當在處理 dragover 事件的代碼部分始終包含 preventDefault() 調用。

可以稍微LookLook。

另外注意,為了創(chuàng)建自定義文件拖動的交互,我們需要在每個拖動事件中調用 event.preventDefault(),也就是阻止默認事件,否則當我們拖拽文件放置的時候會是瀏覽器來打開我們的文件,而不是由拖動事件來處理了。

這里我們可以進行一個統一處理:

;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  // dropArea往下看
  dropArea.addEventListener(eventName, preventDefaults, false);
  document.body.addEventListener(eventName, preventDefaults, false);
});

function preventDefaults(e) {
  // 阻止默認事件
  e.preventDefault();
  // 阻止冒泡
  e.stopPropagation();
}

布局樣式

大概了解下拖動事件后,我們來開始進行布局與樣式,隨便簡簡單單搞一下就可以啦,不是重點。

直接貼代碼:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>拖動上傳</title>
  <style>
    body {
      padding: 0;
      margin: 0;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    #drop-area {
      border: 2px dashed #ccc;
      border-radius: 10px;
      width: 480px;
      font-family: sans-serif;
      margin: 100px auto;
      padding: 20px;
    }
    #drop-area.highlight {
      border-color: #409eff;
    }
    p {
      margin-top: 0;
    }
    #file {
      display: none;
    }
    .button {
      display: block;
      padding: 10px;
      background: #409eff;
      cursor: pointer;
      border-radius: 5px;
      margin-bottom: 10px;
      color: #fff;
      width: fit-content;
    }
    #show-area img {
      width: 150px;
      margin-top: 10px;
      margin-right: 10px;
      vertical-align: middle;
    }
  </style>
</head>
<body>
  <div id="drop-area">
    <p>將文件拖到此處或點擊上載</p>
    <input id="file" type="file" multiple accept="image/*" onchange="handleFiles(this.files)">
    <label class="button" for="file">點擊上載</label>
    <progress id="progress" max=100 value=0></progress>
    <div id="show-area"></div>
  </div>
</body>
</html>

結構和樣式都比較簡單,就關鍵去注意 input 元素上加了一個 onchange 事件與 multiple 允許多文件上傳,還有一些 id 的命名,就沒啦。

拖動功能

接下來,我們進入核心部分 - 拖動。

首先,第一件事,先獲取我們的拖動放置區(qū):

const dropArea = document.getElementById('drop-area');

其次,我們先給放置區(qū)的邊框添加一點拖動時的交互效果,提高用戶體驗。

;['dragenter', 'dragover'].forEach(eventName => {
  dropArea.addEventListener(eventName, highlight, false);
})
;['dragleave', 'drop'].forEach(eventName => {
  dropArea.addEventListener(eventName, unhighlight, false);
})

function highlight(e) {
  dropArea.classList.add('highlight');
}
function unhighlight(e) {
  dropArea.classList.remove('highlight');
}

可以通過簡單的添加與刪除 class 來解決這個問題。

然后,我們來處理文件放置的事件 - drop。

dropArea.addEventListener('drop', dragEvent => {
  // 獲取文件列表
  let files = e.dataTransfer.files;
  handleFiles(files);
}, false)

主要是從中獲取拖動的文件對象列表。

注意,如果你直接去打印 dragEvent 對象,展開后,發(fā)現 dataTransfer.files 為空的話。

你可以再打印 dragEvent.dataTransfer.files 瞧瞧。

有了文件對象后,這個功能我們就完成一大半了。

不過,要注意,上面拿到的文件對象列表 files 不是數組,它是一個偽數組。當我們實現 handleFiles 時,需要特別處理一下。

function handleFiles(files) {
  // 轉換文件對象列表的偽數組
  files = [...files];
  // 將文件對象上傳到服務器
  files.forEach(uploadFile);
}

由于可能有多個文件對象一起上傳,這里我們用了 .forEach 來循環(huán)迭代。

拿到正確的文件對象后,上傳到服務器端就完事了。

function uploadFile(file) {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  formData.append('file', file);
  const url = '上傳地址';
  xhr.open('POST', url, true);
  xhr.addEventListener('readystatechange', function(e) {
    if (xhr.readyState == 4 && xhr.status == 200) {
      // 上傳成功-結束
    }
    else if (xhr.readyState == 4 && xhr.status != 200) {
      // 上傳失敗
    }
  })
  xhr.send(formData);
}

文件預覽

上面,我們完成文件拖動上傳的基本功能,接下來我們來給它進行"增幅",讓它變得更強。

既然是文件上傳,我們肯定是希望有回顯/預覽,這樣才能給用戶提供一個良好的體驗。這里我們以回顯圖片為例,至于,其他文件類型.....Em...不好回顯。

回顯方式有幾種,最簡單的方式就是你可以等圖片上傳后,服務器給你返回URL,你直接顯示就行,但有時圖片很大的話,就意味你要等,或者需要占位符,這就很麻煩了。

而這次我們要探討的替換方案是從 drop 事件接收文件對象,再通過 FileReader API 進行轉換、回顯。不過,這是一個異步的API,你也可以使用 FileReaderSync 進行替換,但是由于我們可以進行多文件上傳,所以還是用異步的叭。

具體過程如下:

function previewFile(file) {
  let reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onloadend = function() {
    let img = document.createElement('img');
    img.src = reader.result;
    document.getElementById('show-area').appendChild(img);
  }
}

那在什么時候使用回顯呢?可以放在 uploadFile 回調方法中進行一個一個回顯。也可以還是丟 handleFiles 方法中,用 .forEach 統一回顯。

function handleFiles(files) {
  files = [...files];
  files.forEach(uploadFile);
  // 回顯文件
  files.forEach(previewFile);
}

上傳進度

最后一個增幅功能,文件上傳進度。

如果只是每次一個一個文件上傳,那很簡單,我們直接監(jiān)聽一下進度事件 progress 就可以完成。

但是,如果是多文件一起上傳,Em......就要稍微費點勁了。

由于我們需要要考慮多文件上傳的情況,所以我們需要來跟蹤記錄兩個關鍵信息:總共要上傳的文件數量(filesTotal)和已經成功上傳的文數量(filesDoneTotal)。有了這兩個數據,我們就能輕松計算出上傳的進度了。

大概代碼的呈現形式如下:

// 初始化進度
function initializeProgress(numfiles) {
  // 重置進度條
  progressBar.value = 0;
  // 重置已上傳數量
  filesDoneTotal = 0;
  // 文件總數量
  filesTotal = numfiles;
}

// 上傳完成
function progressDone() {
  filesDoneTotal++;
  // 計算上傳進度
  progressBar.value = filesDoneTotal / filesTotal * 100;
}

而具體在我們示例中的表現:

function handleFiles(files) {
  files = [...files];
  // 初始化進度
  initializeProgress(files.length);
  files.forEach(uploadFile);
  files.forEach(previewFile);
}

let progressBar = document.getElementById('progress');
// 記錄文件的上傳進度
let uploadProgress = [];

function initializeProgress(numFiles) {
  progressBar.value = 0;
  uploadProgress = [];
  for (let i = numFiles; i > 0; i--) {
    uploadProgress.push(0);
  }
}
function updateProgress(fileNumber, percent) {
  uploadProgress[fileNumber] = percent;
  let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length;
  progressBar.value = total;
}

應該比較好理解吧?

initializeProgressupdateProgress 兩個方法就是上面先講的兩個方法放到實際業(yè)務中的變化而已。

實際使用:

function uploadFile(file) {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  formData.append('file', file);
  const url = '上傳地址';
  xhr.open('POST', url, true);
  
  // 監(jiān)聽上傳進度事件
  xhr.upload.addEventListener("progress", function (e) {
    // e.loaded為上傳的字節(jié)數,e.total為總的文件字節(jié)數
    updateProgress(i, (e.loaded * 100.0 / e.total) || 100);
  });
  
  xhr.addEventListener('readystatechange', function(e) {
    if (xhr.readyState == 4 && xhr.status == 200) {
      // 上傳完成,i為每個文件序號,其實就是下標
      updateProgress(i, 100);
    }
    else if (xhr.readyState == 4 && xhr.status != 200) {
      // 上傳失敗
    }
  })
  xhr.send(formData);
}

關于進度事件 progress 的相關參數信息,可以再細致瞧瞧。傳送門

完整源碼

最后,貼貼完整代碼過程,你可以直接復制去玩玩看。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>拖動上傳</title>
  <style>
    body {
        padding: 0;
        margin: 0;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    #drop-area {
        border: 2px dashed #ccc;
        border-radius: 10px;
        width: 480px;
        font-family: sans-serif;
        margin: 100px auto;
        padding: 20px;
    }
    #drop-area.highlight {
        border-color: #409eff;
    }
    p {
        margin-top: 0;
    }
    #file {
        display: none;
    }
    .button {
        display: block;
        padding: 10px;
        background: #409eff;
        cursor: pointer;
        border-radius: 5px;
        margin-bottom: 10px;
        color: #fff;
        width: fit-content;
    }
    #show-area img {
        width: 150px;
        margin-top: 10px;
        margin-right: 10px;
        vertical-align: middle;
    }
  </style>
</head>

<body>
  <div id="drop-area">
    <p>將文件拖到此處或點擊上載</p>
    <input id="file" type="file" multiple accept="image/*" onchange="handleFiles(this.files)">
    <label class="button" for="file">點擊上載</label>
    <progress id="progress" max=100 value=0></progress>
    <div id="show-area" />
  </div>

  <script>
    const dropArea = document.getElementById('drop-area');
    ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, preventDefaults, false)
        document.body.addEventListener(eventName, preventDefaults, false)
    })
    ;['dragenter', 'dragover'].forEach(eventName => {
        dropArea.addEventListener(eventName, highlight, false)
    })

    ;['dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, unhighlight, false)
    })
    dropArea.addEventListener('drop', (e) => {
        let files = e.dataTransfer.files
        handleFiles(files);
    }, false)
    function preventDefaults(e) {
        e.preventDefault()
        e.stopPropagation()
    }
    function highlight(e) {
        dropArea.classList.add('highlight')
    }
    function unhighlight(e) {
        dropArea.classList.remove('highlight')
    }
    let uploadProgress = []
    let progressBar = document.getElementById('progress')
    function initializeProgress(numFiles) {
        progressBar.value = 0
        uploadProgress = []
        for (let i = numFiles; i > 0; i--) {
            uploadProgress.push(0)
        }
    }
    function updateProgress(fileNumber, percent) {
        uploadProgress[fileNumber] = percent
        let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
        progressBar.value = total
    }
    function handleFiles(files) {
        files = [...files]
        initializeProgress(files.length)
        files.forEach(uploadFile)
        files.forEach(previewFile)
    }
    function previewFile(file) {
        let reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onloadend = function () {
            let img = document.createElement('img')
            img.src = reader.result
            document.getElementById('show-area').appendChild(img)
        }
    }
    function uploadFile(file, i) {
        setTimeout(() => {
            updateProgress(i, 20 || 100)
        }, 500)
        setTimeout(() => {
            updateProgress(i, 50 || 100)
        }, 800)
        setTimeout(() => {
            updateProgress(i, 80 || 100)
        }, 1000)
        setTimeout(() => {
            updateProgress(i, 100)
        }, 1500)
    }
  </script>
</body>
</html>

實際上傳部分,為了演示效果,小編使用 setTimeout 延時先頂替著用用吧。

到此這篇關于JavaScript實現多文件拖動上傳功能的文章就介紹到這了,更多相關JavaScript多文件拖動上傳內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • bcastr2.0 通用的圖片瀏覽器

    bcastr2.0 通用的圖片瀏覽器

    bcastr2.0 通用的圖片瀏覽器...
    2006-11-11
  • javascript使用call調用微信API

    javascript使用call調用微信API

    這篇文章主要介紹了javascript使用call調用微信API,需要的朋友可以參考下
    2014-12-12
  • javaScript 刪除字符串空格多種方法小結

    javaScript 刪除字符串空格多種方法小結

    在js中自一些字符串刪除空格的功能,本文章來介紹了字符串刪除功能加利用正則快速替換字符串中所有空格實現代碼
    2012-10-10
  • 百度判斷手機終端并自動跳轉js代碼及使用實例

    百度判斷手機終端并自動跳轉js代碼及使用實例

    這篇文章主要介紹了百度判斷手機終端并自動跳轉js代碼及使用實例,需要的朋友可以參考下
    2014-06-06
  • 自定義require函數讓瀏覽器按需加載Js文件

    自定義require函數讓瀏覽器按需加載Js文件

    可能很多人看到這個題目就會想到LABjs、RequireJS、SeaJS... 這些庫,但無奈小編沒用過這些庫,什么 AMD 、CMD 哪來那么多術語... 前端的庫太多了,要看各種亂七八糟的文檔,好難啊,還不如自己寫一個庫呢。于是就有了這文章,有需要的朋友們下面來一起學習學習吧。
    2016-11-11
  • 溫習Javascript基礎語法之詞法結構

    溫習Javascript基礎語法之詞法結構

    javascript是一門簡單的語言,也是一門復雜的語言。這篇文章主要介紹了溫習Javascript基礎語法之詞法結構的相關資料,需要的朋友可以參考下
    2016-05-05
  • JS扁平化輸出數組的2種方法解析

    JS扁平化輸出數組的2種方法解析

    這篇文章主要介紹了JS扁平化輸出數組的2種方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • js根據需要計算數組中重復出現某個元素的個數

    js根據需要計算數組中重復出現某個元素的個數

    今天小編就為大家分享一篇關于js根據需要計算數組中重復出現某個元素的個數,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • 關于JS中的箭頭函數

    關于JS中的箭頭函數

    這篇文章主要介紹了關于JS中的箭頭函數,ES6中允許使用=>來定義函數。箭頭函數相當于匿名函數,并簡化了函數定義,箭頭函數在語法上比普通函數簡潔多。箭頭函數就是采用箭頭=>來定義函數,省去關鍵字function,需要的朋友可以參考下
    2023-05-05
  • JS 調用微信掃一掃功能

    JS 調用微信掃一掃功能

    這篇文章主要介紹了JS 調用微信掃一掃功能,本文給大家介紹的非常詳細,具有參考借鑒價值,需要的朋友參考下吧
    2016-12-12

最新評論

明溪县| 称多县| 丰台区| 莱西市| 安陆市| 中江县| 上思县| 桐城市| 古丈县| 隆子县| 嘉定区| 同心县| 宁陵县| 新闻| 石棉县| 湖南省| 旅游| 西华县| 大竹县| 马龙县| 游戏| 天津市| 宜丰县| 富平县| 尼勒克县| 绵竹市| 红桥区| 克山县| 盖州市| 任丘市| 尚义县| 沧州市| 合水县| 红河县| 涟源市| 武城县| 柘荣县| 晋城| 隆子县| 广饶县| 隆尧县|