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

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...