前端實現複製文字和圖片,原來這麼簡單!

来源:https://www.cnblogs.com/zhouchenkai/archive/2023/08/29/17641211.html
-Advertisement-
Play Games

# el-autocomplete核心參數 可以實現非同步的數據拉取,從非同步返回的數據中,選擇需要的結果,並回顯到文本框中。 ## fetch-suggestions 回調列表,非同步的方式獲取數據列表,顯示在列表框中 ## @select 當選中某一項時,會觸發這個方法,將數據獲取到,這時,我們可以將 ...


1.功能需求

實習工作中,遇到一個需求,需要完成點擊複製的功能,當文字過長的時候,讓用戶手拖再ctrl+c這種方式體驗就不是很好了,如果可以點擊一下直接複製就是一種不錯的優化用戶體驗的方式。

經過查閱文檔,網路上完成這個功能大多使用兩大類方法

第一種是以document.execCommand() 方法為主,無論是手寫還是使用clipboard.js插件都是依賴的這個方法,但是在MDN 文檔中已經顯示過時了。

第二種是用了navigator.clipboard的方法,避免了過時問題,但是在複製圖片的時候會有一定的瀏覽器相容性問題

 2.document.execCommand('copy') 

這個方法其實就是在模擬用戶選擇元素然後右鍵複製的動作。儘管MDN已經顯示這個方法過時了,但是僅針對copy這個指令,大部分主流瀏覽器都可以支持,所以這個方法仍然可以作為一種實現問題的方案。

2.1 基本用法

根據MDN文檔學習本方法的傳參和返回值

語法

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

這個方法可以傳3個參數,並且會返回一個布爾值

返回值

先從返回值開始,返回值相對比較簡單,如果返回的值是false就表示瀏覽器不支持使用這個操作,反之瀏覽器支持該操作就返回true。

雖然這個返回值看似可以用來提前判斷瀏覽器相容性,但是文檔中不推薦在調用一個命令前,嘗試使用返回值去校驗瀏覽器的相容性

參數值

參數一共可以傳3個,但是使用複製命令的時候只需要傳第一個參數就可以。這裡簡單介紹一下3個參數

  1. aCommandName:一個字元串類型的參數,是命令的名稱,比如複製用到的copy,剪切用到的cut
  2. aShowDefaultUI:一個布爾類型的參數,表示是否展示用戶界面,一般為false,Mozilla 沒有實現
  3. aValueArgument:一些命令(例如 insertImage)需要額外的參數(insertImage 需要提供插入 image 的 url),預設為 null。

簡單舉例

以本文主要講的複製命令為例子:document.execCommand('copy')

指令相容性問題

前文講到,MDN不推薦在調用一個命令前,嘗試使用返回值去校驗瀏覽器的相容性,那麼就需要用另外的方法去檢測瀏覽器是否支持某個指令,瀏覽器為我們提供了一個方法叫document.queryCommandSupported(),使用這個方法可以檢測瀏覽器是否支持某個指令,這個方法比較簡單,只有1個參數,參數就傳指令字元串,方法的返回值是一個布爾值表示當前瀏覽器是否支持這個指令。

舉例如下:

 

    if(document.queryCommandSupported && document.queryCommandSupported('copy')){
        //先檢測是否支持document.queryCommandSupported和copy指令
        //如果都支持直接執行指令
        document.execCommand('copy')
    }

 

MDN文檔中提到,document.queryCommandSupported也被棄用了,但是為了相容性依然保留可用,當我們使用document.execCommand的時候仍然可以用document.queryCommandSupported來檢測是否支持。同時,它的瀏覽器相容性也是比較好的,大部分主流瀏覽器都支持。

 

2.2 Selection Api

複製文本這個操作對比複製圖片是相對比較簡單的,一共包含2大步

一是選中要複製的元素

二是執行複製指令。

執行複製指令在前面的基本語法里已經講到了,直接調用document.execCommand('copy')就可以了。剩下要做的便是先選中元素了。下麵便介紹一下和選中元素相關的selection api

MDN文檔上寫道:Selection 對象表示用戶選擇的文本範圍或插入符號的當前位置。它代表頁面中的文本選區,可能橫跨多個元素。文本選區由用戶拖拽滑鼠經過文字而產生。如果要獲取用於檢查或修改的 Selection 對象,可以調用 window.getSelection() 方法。

這看起來就十分的官方和抽象,簡單的來說Selection 對象所對應的是用戶所選擇的 ranges (區域),俗稱 拖藍。上圖中的拖藍就是selection對象中的一個區域。

通過getRangeAt方法可以獲取到具體的選中區域

    let selection = window.getSelection() //獲取selection對象
    let range = selection.getRangeAt(0)  //獲取第一個選中的區域

 除了獲取選區中的區域之外,我們還可以通過 document.createRange()創建一個新的區域,然後將該區域添加到選區中

<body>
    <div id="hello">你好</div>
    <div id="yes">是的</div>
</body>
<script>
    let selection = window.getSelection() //獲取selection對象
    const hello = document.querySelector('#hello')
    if(selection.rangeCount > 0){
        //如果有已經選中的區域,直接全部去除
        selection.removeAllRanges()
    }
    let range = document.createRange() //創建range
    range.selectNode(hello) //range選中hello
    selection.addRange(range) //加入到選區中
</script>

 

效果如下,當代碼執行後,你好這個元素被直接選中

加入區域的api包括range.selectNode和range.selectNodeContents。其中selectNode表示選中整個節點而selectNodeContents表示選中節點中的內容,針對文字的複製需要選中節點的內容,而圖片的複製需要選中節點本身。

用法如下

<body>
    <div id="hello">你好</div>
    <div id="yes">是的</div>
    <button class="btn">點擊複製</button>
</body>
<script>
    const yes = document.querySelector('#yes')
    const selection = window.getSelection()
    const range= document.createRange()
    range.selectNode(yes)
    range.selectNode(yes)
</script>

 

 

2.3複製文字

通過以上的selection api可以完成 創建selection對象-->選中節點內容-->添加到區域-->執行一下copy指令就可以完成複製文字了

<body>
    <div id="hello">你好</div>
    <div id="yes">是的</div>
    <button class="btn">點擊複製</button>
</body>

<script>
    const btn = document.querySelector('.btn')
    const hello = document.querySelector('#hello')
    btn.addEventListener('click', () => {
        let range = document.createRange() //創建range
        range.selectNodeContents(hello) //range選中hello
        let selection = window.getSelection() //獲取selection對象
        if (selection.rangeCount > 0) {
            //如果有已經選中的區域,直接全部去除
            selection.removeAllRanges()
        }
        selection.addRange(range) //加入到選區中
        if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
            //先檢測是否支持document.queryCommandSupported和copy指令
            //如果都支持直接執行指令
            document.execCommand('copy')
            //去除選中區域,取消拖藍效果
            selection.removeAllRanges()
        }
    })
</script>

 

2.4複製圖像

複製圖像的操作是和複製文字基本相同的,只是需要在加入區域時選中整個節點,也就是把selectNodeContents方法換成selectNode

<body>
    <div id="hello">你好</div>
    <div id="yes">是的</div>
    <img src="./test.png" alt="">
    <button class="btn">點擊複製</button>
</body>
<script>
    const btn = document.querySelector('.btn')
    const hello = document.querySelector('#hello')
    const img = document.querySelector('img')
    btn.addEventListener('click', () => {
        let range = document.createRange() //創建range
        range.selectNode(img) //range選中圖像節點
        let selection = window.getSelection() //獲取selection對象
        if (selection.rangeCount > 0) {
            //如果有已經選中的區域,直接全部去除
            selection.removeAllRanges()
        }
        selection.addRange(range) //加入到選區中
        if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
            //先檢測是否支持document.queryCommandSupported和copy指令
            //如果都支持直接執行指令
            document.execCommand('copy')
            //去除選中區域,取消拖藍效果
            selection.removeAllRanges()
        }
    })
</script>

 

3.clipboard.js

clipboard.js是一個第三方庫,也是使用了前文所講到的document.execCommand('copy')來實現的點擊複製,使用方便,但是只能用於文本的複製。

3.1安裝和引入clipboard.js

使用npm安裝

npm install clipboard --save

安裝後在html文件內引入

<script src="dist/clipboard.min.js"></script>

或者使用CDN引入(這裡只寫了一種CDN引入方式,可以選擇多種不同CDN方,具體請看https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"></script>

使用import的方式引入

import Clipboard from "clipboard";

3.2基本使用

初始化

直接創建一個ClipboardJS對象,傳的參數可以是選擇器字元串或者是DOM元素或者是DOM元素列表

new ClipboardJS('.btn') // import方式為 new Clipboard('.btn')

 

實現點擊複製文字功能

初始化完後,可以到要綁定的對應元素下添加data-clipboard-target屬性,屬性值是要複製的元素的選擇器,這裡要複製的元素是 ‘是的’ 那個div,所以屬性值就寫#yes。不進行其他配置時,我們點擊按鈕,觸發點擊事件後,就可以完成複製文字 ‘是的’ 了。

<body>
    <div id="hello" >你好</div>
    <div id="yes">是的</div>
    <button class="btn" data-clipboard-target="#yes">點擊複製</button>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"></script>
<script>
    new ClipboardJS('.btn') // import方式為 new Clipboard('.btn')
</script>

 點擊後,是的這個元素被選中(拖藍),使用ctrl+v可以完成文字的複製,效果已經達到。

此時有2個問題需要優化

  1. 複製的內容必須是頁面上的DOM元素,能不能是自己設定的?
  2. 拖藍的效果不是很好看,如何複製文字不顯示選中效果?

這時就要用到一個新的屬性data-clipboard-text,屬性值就是希望動態複製的內容。對ClipboardJS綁定的元素設置這個屬性就可以動態複製自己設定的內容,此時就不需要再設置data-clipboard-target屬性了(如果同時寫2個屬性,data-clipboard-text優先)。以下代碼是一個寫死的簡單展示,真實使用的時候屬性值要用js設置成需要複製的值。

<body>
    <div id="hello" >你好</div>
    <div id="yes">是的</div>
    <button class="btn" data-clipboard-text="動態設置的內容">點擊複製</button>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"></script>
<script>
    new ClipboardJS('.btn') // import方式為 new Clipboard('.btn')
</script>

 上圖顯示點擊之後,複製內容成功,這樣沒有選中元素的效果,不會拖藍,交互效果更好的同時又能動態設置內容

3.3更多用法

data-clipboard-action屬性

data-clipboard-action屬性可以決定執行的操作,這個屬性有2個可選值copy或者是cut,預設是copy也就是複製,前面的所有代碼中都沒有出現這個屬性,是直接使用的預設值copy。cut剪切,只能在input和textarea標簽中使用,顯然之前的div標簽是無法使用的。使用方法仍是對ClipboardJS綁定的元素設置這個屬性。

<button class="btn" data-clipboard-text="動態設置的內容" data-clipboard-action="copy">點擊複製</button>

事件處理

事件處理可以讓用戶設置複製或剪切成功或者失敗的回調,事件名分別是success和error。可以通過on在ClipboardJS實例對象身上綁定success和error事件處理的回調。以下示例寫了最簡單alert列印成功和失敗

    const clipboard = new ClipboardJS('.btn') // import方式為 new Clipboard('.btn')
    clipboard.on('success',function(){
        alert('複製成功')
    })
    clipboard.on('error',function(){
        alert('複製失敗')
    })

純JS寫法

如果不想改HTML,加入過多的屬性,可以直接使用純JS寫法來初始化ClipboardJS對象構造函數中傳入第二個參數,第二個參數為對象,如下的示例中僅用完成js就完成了動態設置複製內容。設置配置對象的text方法,返回值就是要複製的內容

    new ClipboardJS('.btn',{
        text: function(){
            return '動態複製的內容'
        }
    })

設置配置對象的target方法,返回值就是要複製的元素

    new ClipboardJS('.btn',{
        target: function (){
            return document.querySelector('#hello')
        }
    })

經過測試,當html中設置屬性的同時,又在構造函數裡加入配置項,以js構造函數配置項為準(優先順序高)

<body>
    <div id="hello">你好</div>
    <div id="yes">是的</div>
    <button class="btn" data-clipboard-target="#hello">點擊複製</button>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"></script>
<script>
    new ClipboardJS('.btn',{
        target(){
            return document.querySelector('#yes')
        }
    })
</script>

銷毀對象

如果使用的是單頁應用程式,可能希望更精確地管理DOM的生命周期。可以使用destroy方法銷毀對象

var clipboard = new ClipboardJS('.btn');
clipboard.destroy();

3.4源碼分析

看了之前的api,想瞭解一下這個所謂的簡單的複製庫是如何實現的,於是打開了源碼開始分析一下

源碼地址 https://github.com/zenorocha/clipboard.js

初始化

構造函數裡面傳2個參數,第一個trigger即觸發點擊的元素對象,第二個options配置項。從最簡單的例子來看,只需要傳一個trigger參數就可以實現功能,那就先不管options,直接看與trigger有關的listenClick方法。

listenClick方法調用了一個第三方庫的listen方法綁定了click事件和對應的回調函數this.onClick,在onclick方法中,調用了ClipboardActionDefault方法,並且傳了對應的幾個配置項參數,action container,target,text,這些值都是this.xxx方法,這幾個方法又是在哪定義的呢?

 

找了一下類內部,定義這些方法的地方是在前文構造函數里的this.resolveOptions方法里

resolveOptions方法里的defaultAction,defaultText等等方法都是類似的,都是調用了一個getAttributeValue方法去獲取html模板上的屬性值

getAttributeValue方法如下,比較簡單 

ClipboardActionDefault

上面跳了這麼多方法雖然不難,但是也有點繞,主要還是在乾一件事,那就是通過定義來準備好ClipboardActionDefault這個方法的參數。這時候就要看一下ClipboardActionDefault這個方法在乾什麼。

簡單來看,這個方法主要分4個if判斷,前2個if就是一些條件的判斷,判斷action只能是複製或者剪切,還有就是判斷要複製的目標節點的節點類型和readonly問題等等,此處不展開去研究,有興趣者可以點擊本部分開始處的源碼鏈接下載。

後2個if判斷中的內容如下,分別用於判斷是否有text值和target值,這2個值也是通過本庫的核心屬性data-clipboard-text和data-clipboard-target在html中獲取的(或者在js配置項里獲取)。判斷完後就調用了ClipboardActionCopy或者ClipboardActionCut方法去實現複製或者剪切功能。

ClipboardActionCopy

這個方法就開始進行文本的複製了,首先判斷要複製的目標是普通的字元串(通過data-clipboard-text設置)還是節點(通過data-clipboard-target設置),如果是文本或者不是普通的輸入元素,直接調用fakeCopyAction方法執行複製操作。

 

 fakeCopyAction先創建了虛擬元素,然後把這個元素插入dom里,最後執行選中+複製操作

創建虛擬元素的方法也比較簡單,先通過原生方法createElement創建了一個textarea元素,然後把它隱藏。創建這種輸入類元素的好處就是可以直接去修改它的value,最後一步操作就是把文本text賦值給textarea

 創建完虛擬元素就要處理選中問題了,這裡調用了select方法,方法內部根據3種元素類型設置了不同的處理對策,select元素只要focus後賦值就好。輸入元素可以調用原生的select方法來選中元素,而普通元素就需要使用之前講到的selection api去獲取range和添加range了

function select(element) {
    var selectedText;

    if (element.nodeName === 'SELECT') {
        //針對select元素的處理
        element.focus();

        selectedText = element.value;
    }
    else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
        //選中輸入元素
        var isReadOnly = element.hasAttribute('readonly');

        if (!isReadOnly) {
            element.setAttribute('readonly', '');
        }

        element.select();
        element.setSelectionRange(0, element.value.length);

        if (!isReadOnly) {
            element.removeAttribute('readonly');
        }

        selectedText = element.value;
    }
    else {
        //普通元素選中
        if (element.hasAttribute('contenteditable')) {
            element.focus();
        }

        var selection = window.getSelection();
        var range = document.createRange();

        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);

        selectedText = selection.toString();
    }

    return selectedText;
}

最後的command('copy')也就是對執行複製指令這個方法的簡單封裝,做了一下相容性的處理。

4. navigator.clipboard

前面的document.execCommand和第三方庫clipboard.js都非常的好用,但是他們可能面臨被棄用的風險,那麼該怎麼解決複製粘貼這個問題呢? H5新推出的clipboard api是 處理複製粘貼相關的api,可以很好的解決這個問題。用promise的方式把數據寫入剪貼板,避免了頁面的卡頓。

4.1 複製文字

Clipboard對象

使用Clipboard api時我們不需要手動創建Clipboard對象,而是通過navigator.clipboard來獲取

列印出Clipboard對象後可以看出,這個對象有4個方法,分為兩大類,write和read類。其中與複製相關的是write類表示把數據寫入剪貼板,和粘貼相關的是read類表示從剪貼板裡面讀取數據

 writeText方法

Clipboard對象中的writeText方法可以用於複製文字,也是非常簡單易用的一個方法。

參數:傳一個字元串參數,即要複製的內容

返回值: 一個promise對象,如果成功複製則是成功的promise,如果寫入剪貼板失敗(複製失敗)則是失敗的promise

示例如下:先創建了一個clipboard對象,然後直接調用writeText方法複製文字123

navigator.clipboard.writeText('123')

根據之前的html結構,使用Clipboard api完成文字的複製

預設情況下,會為當前的激活的頁面自動授予剪貼板的寫入許可權。出於安全方面考慮,這裡我們還是先主動向用戶請求剪貼板的寫入許可權,如果被授權,就可以調用上面的方法直接完成複製了。

<body>
    <div id="hello">你好</div<

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ![file](https://img2023.cnblogs.com/other/2685289/202308/2685289-20230829152524057-1800624819.png) 作者 | sqlboy-yuzhenc ## 背景介紹 在實際應用中,我們經常需要將特定的任務通知給特 ...
  • NineData和SelectDB即將聯合舉辦線上發佈會,主題為“實時數據驅動,引領企業智能化數據管理”。SelectDB產品副總裁薑國強將介紹雲原生存算分離版本的SelectDBCloud,以及ApacheDoris的未來發展趨勢。玖章算術技術副總裁陳長城將介紹NineData的產品架構和數據複製... ...
  • 如今,大規模、高時效、智能化數據處理已是“剛需”,企業需要更強大的數據平臺,來應對數據查詢、數據處理、數據挖掘、數據展示以及多種計算模型並行的挑戰,湖倉一體方案應運而生。 《實時湖倉實踐五講》是袋鼠雲打造的系列直播活動,將圍繞實時湖倉的建設趨勢和通用問題,邀請奮戰於企業數字化一線的核心產品&技術專家 ...
  • 原子化 CSS 框架 我記得很久之前有時候為了少寫些css,我們通常會有如下的樣板代碼 .block { display: block; } .flex { display:flex } .flex-center { align-items: center; justify-content: cen ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 記憶體泄漏是前端開發中的一個常見問題,可能導致項目變得緩慢、不穩定甚至崩潰。在本文中,我們將深入探討在JavaScript、Vue和React項目中可能導致記憶體泄漏的情況,並提供詳細的代碼示例,以幫助開發人員更好地理解和解決這些問題。 第一 ...
  • 基於傳統認知,前端產品直接觸達消費者,往往具有高度的定製化、需求變更頻繁等特點,要求具有很好的動態性, 能夠滿足不同客戶的需求。那麼能否建設類似的前端中台產品,我們姑且稱之為“前端領域產品”,實現接入團隊端到端能力復用呢?我們在撮合業務線中進行了一系列思考和探索。 ...
  • 1. 使用 defineStore 創建一個 store, 每個 store 要設置一個唯一 id; ```ts import { defineStore } from 'pinia' import { ref } from 'vue' // useStore 可以是 useUser、useCart ...
  • 通過這個示例,你將更深入地瞭解如何在實際業務中應用Flutter,以及如何運用不同的解決方案和技術來構建高效、穩定的應用。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...