最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
這個29.7K的剪貼板JS庫有點東西!

本期阿寶哥將介紹一個被 157317 個項目引用的 JS 開源庫 —— clipboard.js。相信挺多小伙伴在項目中,也用到了這個庫。那么這個庫背后的工作原理是什么?感興趣的小伙伴,跟阿寶哥一起來揭開這背后的秘密吧。

成都創(chuàng)新互聯(lián)總部坐落于成都市區(qū),致力網(wǎng)站建設服務有網(wǎng)站設計、成都網(wǎng)站制作、網(wǎng)絡營銷策劃、網(wǎng)頁設計、網(wǎng)站維護、公眾號搭建、小程序設計、軟件開發(fā)等為企業(yè)提供一整套的信息化建設解決方案。創(chuàng)造真正意義上的網(wǎng)站建設,為互聯(lián)網(wǎng)品牌在互動行銷領域創(chuàng)造價值而不懈努力!

一、clipboard.js 簡介

clipboard.js 是一個用于將文本復制到剪貼板的 JS 庫。沒有使用 Flash,沒有使用任何框架,開啟 gzipped 壓縮后僅僅只有 3kb。

(圖片來源:https://clipboardjs.com/#example-text)

那么為什么會有 clipboard.js 這個庫呢?因為作者 zenorocha 認為:

  • 將文本復制到剪貼板應該不難。它不需要幾十個步驟來配置,也不需要加載數(shù)百 KB 的文件。最最重要的是,它不應該依賴于 Flash 或其他任何框架。

該庫依賴于 Selection 和 execCommand API,幾乎所有的瀏覽器都支持 Selection API,然而 execCommand API 卻存在一定的兼容性問題:

(圖片來源:https://caniuse.com/?search=execCommand)

(圖片來源:https://caniuse.com/?search=execCommand)

當然對于較老的瀏覽器,clipboard.js 也可以優(yōu)雅地降級。好的,現(xiàn)在我們來看一下如何使用 clipboard.js。

二、clipboard.js 使用

在使用 clipboard.js 之前,你可以通過 NPM 或 CDN 的方式來安裝它:

NPM

 
 
 
 
  1. npm install clipboard --save 

CDN

 
 
 
 
  1.  

clipboard.js 使用起來很簡單,一般只要 3 個步驟:

1.定義一些標記

 
 
 
 
  1.  
  2. 復制 

2.引入 clipboard.js

 
 
 
 
  1.  

3.實例化 clipboard

 
 
 
 
  1.  

以上代碼成功運行之后,當你點擊 “復制” 按鈕時,輸入框中的文字會被選中,同時輸入框中的文字將會被復制到剪貼板中,對應的效果如下圖所示:

除了 input 元素之外,復制的目標還可以是 div 或 textarea 元素。在以上示例中,我們復制的目標是通過 data-* 屬性 來指定。此外,我們也可以在實例化 clipboard 對象時,設置復制的目標:

 
 
 
 
  1. // https://github.com/zenorocha/clipboard.js/blob/master/demo/function-target.html 
  2. let clipboard = new ClipboardJS('.btn', { 
  3.   target: function() { 
  4.     return document.querySelector('div'); 
  5.   } 
  6. }); 

如果需要設置復制的文本,我們也可以在實例化 clipboard 對象時,設置復制的文本:

 
 
 
 
  1. // https://github.com/zenorocha/clipboard.js/blob/master/demo/function-text.html 
  2. let clipboard = new ClipboardJS('.btn', { 
  3.   text: function() { 
  4.     return 'to be or not to be'; 
  5.   } 
  6. }); 

關于 clipboard.js 的使用,阿寶哥就介紹到這里,感興趣的小伙伴可以查看 Github 上 clipboard.js 的使用示例。

由于 clipboard.js 底層依賴于 Selection 和 execCommand API,所以在分析 clipboard.js 源碼前,我們先來了解一下 Selection 和 execCommand API。

三、Selection 與 execCommand API

3.1 Selection API

Selection 對象表示用戶選擇的文本范圍或插入符號的當前位置。它代表頁面中的文本選區(qū),可能橫跨多個元素。文本選區(qū)由用戶拖拽鼠標經(jīng)過文字而產(chǎn)生。如果要獲取用于檢查或修改的 Selection 對象,可以調(diào)用 window.getSelection 方法。

Selection 對象所對應的是用戶所選擇的 ranges (區(qū)域),俗稱 拖藍。默認情況下,該函數(shù)只針對一個區(qū)域,我們可以這樣使用這個函數(shù):

 
 
 
 
  1. let selection = window.getSelection(); 
  2. let range = selection.getRangeAt(0); 

以上示例演示了如何獲取選區(qū)中的第一個區(qū)域,其實除了獲取選區(qū)中的區(qū)域之外,我們還可以通過 createRange API 創(chuàng)建一個新的區(qū)域,然后將該區(qū)域添加到選區(qū)中:

 
 
 
 
  1. 大家好,我是阿寶哥。歡迎關注全棧修仙之路
     
  2.  

 以上代碼用于選中頁面中所有的 strong 元素,但需要注意的是,目前只有使用 Gecko 渲染引擎的瀏覽器,比如 Firefox 瀏覽器實現(xiàn)了多個區(qū)域。

在某些場景下,你可能需要獲取選中區(qū)域中的文本。針對這種場景,你可以通過調(diào)用 Selection 對象的 toString 方法來獲取被選中區(qū)域中的純文本。

3.2 execCommand API

document.execCommand API 允許運行命令來操作網(wǎng)頁中的內(nèi)容,常用的命令有 bold、italic、copy、cut、delete、insertHTML、insertImage、insertText 和 undo 等。下面我們來看一下該 API 的語法:

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

相關的參數(shù)說明如下:

  • aCommandName:字符串類型,用于表示命令的名稱;
  • aShowDefaultUI:布爾類型,用于表示是否展示用戶界面,一般為 false;
  • aValueArgument:額外參數(shù),一些命令(比如 insertImage)需要額外的參數(shù)(提供插入圖片的 URL),默認為 null。

調(diào)用 document.execCommand 方法后,該方法會返回一個布爾值。如果是 false 的話,表示操作不被支持或未被啟用。對于 clipboard.js 這個庫來說,它會通過 document.execCommand API 來執(zhí)行 copy 和 cut命令,從而實現(xiàn)把內(nèi)容復制到剪貼板。

那么現(xiàn)在問題來了,我們有沒有辦法判斷當前瀏覽器是否支持 copy 和cut 命令呢?答案是有的,即使用瀏覽器提供的 API —— Document.queryCommandSupported,該方法允許我們確定當前的瀏覽器是否支持指定的編輯命令。

clipboard.js 這個庫的作者,也考慮到了這種需求,所以提供了一個靜態(tài)的 isSupported 方法,用于檢測當前的瀏覽器是否支持指定的命令:

 
 
 
 
  1. // src/clipboard.js 
  2. static isSupported(action = ['copy', 'cut']) { 
  3.   const actions = (typeof action === 'string') ? [action] : action; 
  4.   let support = !!document.queryCommandSupported; 
  5.  
  6.   actions.forEach((action) => { 
  7.     support = support && !!document.queryCommandSupported(action); 
  8.   }); 
  9.  
  10.   return support; 

Document.queryCommandSupported 兼容性較好,大家可以放心使用,具體的兼容性如下圖所示:

(圖片來源:https://caniuse.com/?search=queryCommandSupported)

介紹完 Selection、execCommand 和 queryCommandSupported API,接下來我們開始分析 clipboard.js 的源碼。

四、clipboard.js 源碼解析

4.1 Clipboard 類

看源碼的時候,阿寶哥習慣從最簡單的用法入手,這樣可以快速地了解內(nèi)部的執(zhí)行流程。下面我們來回顧一下前面的示例:

 
 
 
 
  1.  
  2.  
  3. 復制 
  4.  
  5.  
  6.  

通過觀察以上的代碼,我們可以快速地找到切入點 —— new ClipboardJS('.btn')。在 clipboard.js 項目內(nèi)的 webpack.config 配置文件中,我們可以找到 ClipboardJS 的定義:

 
 
 
 
  1. module.exports = { 
  2.   entry: './src/clipboard.js', 
  3.   mode: 'production', 
  4.   output: { 
  5.     filename: production ? 'clipboard.min.js' : 'clipboard.js', 
  6.     path: path.resolve(__dirname, 'dist'), 
  7.     library: 'ClipboardJS', 
  8.     globalObject: 'this', 
  9.     libraryExport: 'default', 
  10.     libraryTarget: 'umd' 
  11.   }, 
  12.   // 省略其他配置信息 

基于以上的配置信息,我們進一步找到了 ClipboardJS 指向的構造函數(shù):

 
 
 
 
  1. import Emitter from 'tiny-emitter'; 
  2. import listen from 'good-listener'; 
  3.  
  4. class Clipboard extends Emitter { 
  5.   constructor(trigger, options) { 
  6.     super(); 
  7.     this.resolveOptions(options); 
  8.     this.listenClick(trigger); 
  9.   } 

在示例中,我們并沒有設置 Clipboard 的配置信息,所以我們先不用關心 this.resolveOptions(options) 的處理邏輯。顧名思義 listenClick 方法是用來監(jiān)聽 click 事件,該方法的具體實現(xiàn)如下:

 
 
 
 
  1. listenClick(trigger) { 
  2.   this.listener = listen(trigger, 'click', (e) => this.onClick(e)); 

在 listenClick 方法內(nèi)部,會通過一個第三方庫 good-listener 來添加事件處理器。當目標觸發(fā) click 事件時,就會執(zhí)行對應的事件處理器,該處理器內(nèi)部會進一步調(diào)用 this.onClick 方法,該方法的實現(xiàn)如下:

 
 
 
 
  1. // src/clipboard.js 
  2. onClick(e) { 
  3.   const trigger = e.delegateTarget || e.currentTarget; 
  4.  
  5.   // 為每次點擊事件,創(chuàng)建一個新的ClipboardAction對象 
  6.   if (this.clipboardAction) { 
  7.     this.clipboardAction = null; 
  8.   } 
  9.   this.clipboardAction = new ClipboardAction({ 
  10.     action    : this.action(trigger), 
  11.     target    : this.target(trigger), 
  12.     text      : this.text(trigger), 
  13.     container : this.container, 
  14.     trigger   : trigger, 
  15.     emitter   : this 
  16.   }); 

在 onClick 方法內(nèi)部,會使用事件觸發(fā)目標來創(chuàng)建 ClipboardAction對象。當你點擊本示例 復制 按鈕時,創(chuàng)建的 ClipboardAction 對象如下所示:

相信看完上圖,大家對創(chuàng)建 ClipboardAction 對象時,所使用到的方法都有了解。那么 this.action、this.target 和 this.text 這幾個方法是在哪里定義的呢?通過閱讀源碼,我們發(fā)現(xiàn)在 resolveOptions方法內(nèi)部會初始化上述 3 個方法:

 
 
 
 
  1. // src/clipboard.js 
  2. resolveOptions(options = {}) { 
  3.   this.action = (typeof options.action === 'function')  
  4.     ? options.action :  this.defaultAction; 
  5.   this.target = (typeof options.target === 'function')  
  6.     ? options.target : this.defaultTarget; 
  7.   this.text = (typeof options.text === 'function') 
  8.     ? options.text : this.defaultText; 
  9.   this.container = (typeof options.container === 'object')    
  10.     ? options.container : document.body; 

在 resolveOptions 方法內(nèi)部,如果用戶自定義了處理函數(shù),則會優(yōu)先使用用戶自定義的函數(shù),否則將使用 clipboard.js 中對應的默認處理函數(shù)。由于我們在調(diào)用 Clipboard 構造函數(shù)時,并未設置 options 參數(shù),所以將使用默認的處理函數(shù):

由上圖可知在 defaultAction、defaultTarget 和 defaultText 方法內(nèi)部都會調(diào)用 getAttributeValue 方法來獲取事件觸發(fā)對象上自定義屬性,而對應的 getAttributeValue 方法也很簡單,具體代碼如下:

 
 
 
 
  1. // src/clipboard.js 
  2. function getAttributeValue(suffix, element) { 
  3.   const attribute = `data-clipboard-${suffix}`; 
  4.   if (!element.hasAttribute(attribute)) { 
  5.     return; 
  6.   } 
  7.   return element.getAttribute(attribute); 

介紹完 Clipboard 類,接下來我們來重點分析一下 ClipboardAction類,該類會包含具體的復制邏輯。

4.2 ClipboardAction 類

在 clipboard.js 項目中,ClipboardAction 類被定義在 src/clipboard-action.js 文件內(nèi):

 
 
 
 
  1. // src/clipboard-action.js 
  2. class ClipboardAction { 
  3.   constructor(options) { 
  4.     this.resolveOptions(options); 
  5.     this.initSelection(); 
  6.   } 

與 Clipboard 類的構造函數(shù)一樣,ClipboardAction 類的構造函數(shù)會優(yōu)先解析 options 配置對象,然后調(diào)用 initSelection 方法,來初始化選區(qū)。在 initSelection 方法中會根據(jù) text 和 target 屬性來選擇不同的選擇策略:

 
 
 
 
  1. initSelection() { 
  2.   if (this.text) { 
  3.     this.selectFake(); 
  4.   } else if (this.target) { 
  5.     this.selectTarget(); 
  6.   } 

對于前面的示例,我們是通過 data-* 屬性 來指定復制的目標,即 data-clipboard-target="#foo",相應的代碼如下:

 
 
 
 
  1.  
  2. 復制 

所以接下來我們先來分析含有 target 屬性的情形,如果含有 target屬性,則會進入 else if 分支,然后調(diào)用 this.selectTarget 方法:

 
 
 
 
  1. // src/clipboard-action.js 
  2. selectTarget() { 
  3.   this.selectedText = select(this.target); 
  4.   this.copyText(); 

在 selectTarget 方法內(nèi)部,會調(diào)用 select 函數(shù)獲取已選中的文本,該函數(shù)是來自 clipboard.js 作者開發(fā)的另一個 npm 包,對應的代碼如下:

 
 
 
 
  1. // https://github.com/zenorocha/select/blob/master/src/select.js 
  2. function select(element) { 
  3.   var selectedText; 
  4.  
  5.   if (element.nodeName === 'SELECT') { 
  6.     element.focus(); 
  7.     selectedText = element.value; 
  8.   } 
  9.   else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { 
  10.     var isReadOnly = element.hasAttribute('readonly'); 
  11.  
  12.     if (!isReadOnly) { 
  13.       element.setAttribute('readonly', ''); 
  14.     } 
  15.  
  16.     element.select(); 
  17.     element.setSelectionRange(0, element.value.length); 
  18.  
  19.     if (!isReadOnly) { 
  20.       element.removeAttribute('readonly'); 
  21.     }  
  22.       selectedText = element.value; 
  23.     } 
  24.   else { 
  25.     // 省略相關代碼  
  26.   } 
  27.   return selectedText; 

因為在以上示例中,我們復制的目標是 input 元素,所以我們先來分析該分支的代碼。在該分支中,使用了 HTMLInputElement 對象的 select 和 setSelectionRange 方法:

  • select:用于選中一個