精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

這個 29.7 K 的剪貼板 JS 庫有點東西!

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

[[357337]]

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

一、clipboard.js 簡介

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


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

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

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

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


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


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

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

二、clipboard.js 使用

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

NPM

  1. npm install clipboard --save 

CDN

  1. <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js"></script> 

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

1.定義一些標記

  1. <input id="foo" type="text" value="大家好,我是阿寶哥"
  2. <button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">復制</button> 

2.引入 clipboard.js

  1. <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js"></script> 

3.實例化 clipboard

  1. <script> 
  2.   var clipboard = new ClipboardJS('.btn'); 
  3.  
  4.   clipboard.on('success'function(e) { 
  5.     console.log(e); 
  6.   }); 
  7.      
  8.   clipboard.on('error'function(e) { 
  9.     console.log(e); 
  10.   }); 
  11. </script> 

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

除了 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 對象表示用戶選擇的文本范圍或插入符號的當前位置。它代表頁面中的文本選區,可能橫跨多個元素。文本選區由用戶拖拽鼠標經過文字而產生。如果要獲取用于檢查或修改的 Selection 對象,可以調用 window.getSelection 方法。

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

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

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

  1. <div>大家好,我是<strong>阿寶哥</strong>。歡迎關注<strong>全棧修仙之路</strong></div> 
  2. <script> 
  3.    let strongs = document.getElementsByTagName("strong"); 
  4.    let s = window.getSelection(); 
  5.  
  6.    if (s.rangeCount > 0) s.removeAllRanges(); // 從選區中移除所有區域 
  7.    for (let i = 0; i < strongs.length; i++) { 
  8.      let range = document.createRange(); // 創建range區域 
  9.      range.selectNode(strongs[i]); // 讓range區域包含指定節點及其內容 
  10.      s.addRange(range); // 將創建的區域添加到選區中 
  11.    } 
  12. </script> 

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

 

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

3.2 execCommand API

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

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

相關的參數說明如下:

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

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

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

clipboard.js 這個庫的作者,也考慮到了這種需求,所以提供了一個靜態的 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 類

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

  1. <!-- 定義一些標記 --> 
  2. <input id="foo" type="text" value="大家好,我是阿寶哥"
  3. <button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">復制</button> 
  4.  
  5. <!-- 實例化 clipboard --> 
  6. <script> 
  7.   let clipboard = new ClipboardJS('.btn'); 
  8.  
  9.   clipboard.on('success'function(e) { 
  10.     console.log(e); 
  11.   }); 
  12.      
  13.   clipboard.on('error'function(e) { 
  14.     console.log(e); 
  15.   }); 
  16. </script> 

通過觀察以上的代碼,我們可以快速地找到切入點 —— new ClipboardJS('.btn')。在 clipboard.js 項目內的 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 指向的構造函數:

  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 方法是用來監聽 click 事件,該方法的具體實現如下:

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

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

  1. // src/clipboard.js 
  2. onClick(e) { 
  3.   const trigger = e.delegateTarget || e.currentTarget; 
  4.  
  5.   // 為每次點擊事件,創建一個新的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 方法內部,會使用事件觸發目標來創建 ClipboardAction對象。當你點擊本示例 復制 按鈕時,創建的 ClipboardAction 對象如下所示:


相信看完上圖,大家對創建 ClipboardAction 對象時,所使用到的方法都有了解。那么 this.action、this.target 和 this.text 這幾個方法是在哪里定義的呢?通過閱讀源碼,我們發現在 resolveOptions方法內部會初始化上述 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 方法內部,如果用戶自定義了處理函數,則會優先使用用戶自定義的函數,否則將使用 clipboard.js 中對應的默認處理函數。由于我們在調用 Clipboard 構造函數時,并未設置 options 參數,所以將使用默認的處理函數:

由上圖可知在 defaultAction、defaultTarget 和 defaultText 方法內部都會調用 getAttributeValue 方法來獲取事件觸發對象上自定義屬性,而對應的 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 文件內:

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

與 Clipboard 類的構造函數一樣,ClipboardAction 類的構造函數會優先解析 options 配置對象,然后調用 initSelection 方法,來初始化選區。在 initSelection 方法中會根據 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. <input id="foo" type="text" value="大家好,我是阿寶哥"
  2. <button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">復制</button> 

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

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

在 selectTarget 方法內部,會調用 select 函數獲取已選中的文本,該函數是來自 clipboard.js 作者開發的另一個 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:用于選中一個 <textarea> 元素或者一個帶有 text 字段的 <input> 元素里的所有內容。
  • setSelectionRange:用于設定 <input> 或 <textarea> 元素中當前選中文本的起始和結束位置。

在獲取選中的文本之后,selectTarget 方法會繼續調用 copyText 方法來復制文本:

  1. copyText() { 
  2.   let succeeded; 
  3.   try { 
  4.     succeeded = document.execCommand(this.action); 
  5.   } catch (err) { 
  6.     succeeded = false
  7.   } 
  8.   this.handleResult(succeeded); 

前面阿寶哥已經簡單介紹了 execCommand API,copyText 方法內部就是使用這個 API 來復制文本。在完成復制之后,copyText 方法會調用 this.handleResult 方法來派發復制的狀態信息:

  1. handleResult(succeeded) { 
  2.   this.emitter.emit(succeeded ? 'success' : 'error', { 
  3.     action: this.action
  4.     text: this.selectedText, 
  5.     trigger: this.trigger
  6.     clearSelection: this.clearSelection.bind(this) 
  7.   }); 

看到這里有些小伙伴可能會問 this.emitter 對象是來自哪里的?其實 this.emitter 對象也就是 Clipboard 實例:

  1. // src/clipboard.js 
  2. class Clipboard extends Emitter { 
  3.   onClick(e) { 
  4.     const trigger = e.delegateTarget || e.currentTarget; 
  5.     // 省略部分代碼 
  6.     this.clipboardAction = new ClipboardAction({ 
  7.       // 省略部分屬性 
  8.       trigger   : trigger
  9.       emitter   : this // Clipboard 實例 
  10.     }); 
  11.   } 

而對于 handleResult 方法派發的事件,我們可以通過 clipboard 實例來監聽對應的事件,具體的代碼如下:

  1. let clipboard = new ClipboardJS('.btn'); 
  2.  
  3. clipboard.on('success'function(e) { 
  4.   console.log(e); 
  5. }); 
  6.      
  7. clipboard.on('error'function(e) { 
  8.   console.log(e); 
  9. }); 

在繼續介紹另一個分支的處理邏輯之前,阿寶哥用一張圖來總結一下上述示例的執行流程:

下面我們來介紹另一個分支,即含有 text 屬性的情形,對應的使用示例如下:

  1. // https://github.com/zenorocha/clipboard.js/blob/master/demo/function-text.html 
  2. let clipboard = new ClipboardJS('.btn', { 
  3.   text: function() { 
  4.     return '大家好,我是阿寶哥'
  5.   } 
  6. }); 

當用戶在創建 clipboard 對象時,設置了 text 屬性,則會執行 if 分支的邏輯,即調用 this.selectFake 方法:

  1. // src/clipboard-action.js 
  2. class ClipboardAction { 
  3.   constructor(options) { 
  4.     this.resolveOptions(options); 
  5.     this.initSelection(); 
  6.   } 
  7.    
  8.   initSelection() { 
  9.     if (this.text) { 
  10.       this.selectFake(); 
  11.     } else if (this.target) { 
  12.       this.selectTarget(); 
  13.     } 
  14.   } 

在 selectFake 方法內部,它會先創建一個假的 textarea 元素并設置該元素的相關樣式和定位信息,并使用 this.text 的值來設置 textarea 元素的內容,然后使用前面介紹的 select 函數來獲取已選擇的文本,最后通過 copyText 把文本拷貝到剪貼板:

  1. // src/clipboard-action.js 
  2. selectFake() { 
  3.   const isRTL = document.documentElement.getAttribute('dir') == 'rtl'
  4.  
  5.   this.removeFake(); // 移除事件監聽并移除之前創建的fakeElem 
  6.  
  7.   this.fakeHandlerCallback = () => this.removeFake(); 
  8.   this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true
  9.  
  10.   this.fakeElem = document.createElement('textarea'); 
  11.   // Prevent zooming on iOS 
  12.   this.fakeElem.style.fontSize = '12pt'
  13.   // Reset box model 
  14.   this.fakeElem.style.border = '0'
  15.   this.fakeElem.style.padding = '0'
  16.   this.fakeElem.style.margin = '0'
  17.   // Move element out of screen horizontally 
  18.   this.fakeElem.style.position = 'absolute'
  19.   this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px'
  20.   // Move element to the same position vertically 
  21.   let yPosition = window.pageYOffset || document.documentElement.scrollTop; 
  22.   this.fakeElem.style.top = `${yPosition}px`; 
  23.  
  24.   this.fakeElem.setAttribute('readonly'''); 
  25.   this.fakeElem.value = this.text; 
  26.  
  27.   this.container.appendChild(this.fakeElem); 
  28.  
  29.   this.selectedText = select(this.fakeElem); 
  30.   this.copyText(); 

為了讓大家能夠更直觀了解 selectFake 方法執行后的頁面效果,阿寶哥截了一張實際的效果圖:

其實 clipboard.js 除了支持拷貝 input 或 textarea 元素的內容之外,它還支持拷貝其它 HTML 元素的內容,比如 div 元素:

  1. <div>大家好,我是阿寶哥</div> 
  2. <button class="btn" data-clipboard-action="copy" data-clipboard-target="div">Copy</button> 

針對這種情形,在 clipboard.js 內部仍會利用前面介紹的 select 函數來選中目標元素并獲取需拷貝的內容,具體的代碼如下所示:

  1. function select(element) { 
  2.   var selectedText; 
  3.  
  4.   if (element.nodeName === 'SELECT') { 
  5.       element.focus(); 
  6.       selectedText = element.value; 
  7.   } 
  8.   else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { 
  9.       // 省略相關代碼  
  10.   } 
  11.   else { 
  12.      if (element.hasAttribute('contenteditable')) { 
  13.         element.focus(); 
  14.      } 
  15.  
  16.      var selection = window.getSelection(); // 創建選取 
  17.      var range = document.createRange(); // 新建區域 
  18.  
  19.      range.selectNodeContents(element); // 使新建的區域包含element節點的內容 
  20.      selection.removeAllRanges(); // 移除選取中的所有區域 
  21.      selection.addRange(range); // 往選區中添加新建的區域 
  22.      selectedText = selection.toString(); // 獲取已選中的文本 
  23.     } 
  24.  
  25.     return selectedText; 

在獲得要拷貝的文本之后,clipboard.js 會繼續調用 copyText 方法把對應的文本拷貝到剪貼板。到這里 clipboard.js 的核心源碼,我們差不多都分析完了,希望閱讀本文后,大家不僅了解了 clipboard.js 背后的工作原理,同時也學會了如何利用事件派發器來實現消息通信 及 Selection 和 execCommand API 等相關的知識。

五、參考資源

  • MDN Selection
  • MDN execCommand
  • MDN queryCommandSupported
  • MDN selectNodeContents

 

責任編輯:姜華 來源: 全棧修仙之路
相關推薦

2009-12-18 14:10:29

Ruby訪問剪貼板

2011-08-09 10:27:41

iOS剪貼板

2010-02-02 17:47:59

C++操作剪貼板

2016-05-11 15:01:31

Linux剪貼板管理器

2009-08-10 17:37:54

2021-12-02 10:11:44

鴻蒙HarmonyOS應用

2021-08-30 13:00:40

JS代碼前端

2023-12-21 08:02:31

React DnD拖拽庫組件

2021-03-09 05:48:01

Windows10操作系統21H2

2023-02-06 07:17:22

2022-03-31 22:53:47

Windows 11太陽谷2智能剪貼板

2018-03-23 10:15:28

Windows 10云剪貼板復制粘貼

2025-06-23 08:30:00

webAPI剪貼板

2021-08-29 07:43:43

CopyQ操作系統微軟

2021-11-30 05:37:51

App監聽手機監管

2020-07-02 07:53:59

App操作系統應用

2021-12-12 09:42:48

Windows 11桌面微軟

2024-04-09 08:27:01

Android高效管理數據

2021-07-29 09:55:59

鴻蒙HarmonyOS應用

2009-10-21 10:15:29

VB.NET復制
點贊
收藏

51CTO技術棧公眾號

欧美在线免费观看亚洲| 成人动漫在线一区| xxxx欧美18另类的高清| xxxx国产视频| 欧美男人天堂| 中文字幕在线观看不卡视频| 97超碰人人模人人爽人人看| 国产嫩bbwbbw高潮| 1024精品久久久久久久久| 日韩精品中文字幕一区| 99久久久无码国产精品6| 幼a在线观看| 处破女av一区二区| 国产精品久久综合av爱欲tv| 欧美另类视频在线观看| 国产探花一区| 亚洲成人av片| 国内自拍第二页| 亚洲人成午夜免电影费观看| 自拍偷拍欧美精品| 欧美精品一区二区视频| 中文字幕日韩第一页| 亚洲高清久久| 欧美精品免费在线| 黄免费在线观看| 懂色av一区二区| 欧美精品在欧美一区二区少妇 | 欧美不卡高清| 国产亚洲成av人片在线观看桃| 成人一区二区三区仙踪林| 精品123区| 五月综合激情婷婷六月色窝| 亚洲一区 在线播放| 成人精品一区二区三区免费 | 亚洲一区二区不卡视频| 亚洲AV成人无码一二三区在线| 国产一区二区美女| 国产精品网站大全| 中文字幕一区在线播放| 99精品视频免费观看视频| 九九九久久久久久| 91高清免费观看| 99国产精品一区二区| 一区二区欧美久久| 99久久人妻无码精品系列| 啪啪激情综合网| 亚洲精品国产精品国产自| 精品无码av一区二区三区不卡| 日韩av黄色| 欧美丰满高潮xxxx喷水动漫| 丁香婷婷激情网| 黄色成人免费网| 色综合欧美在线视频区| 欧美黄网站在线观看| 中文不卡1区2区3区| 亚洲高清免费观看高清完整版在线观看| 在线视频一二三区| 超碰在线观看免费| 亚洲综合成人在线视频| 亚洲成人动漫在线| 新版中文在线官网| 亚洲一区二区三区中文字幕 | 日韩不卡一二三区| 国产精品电影一区| 免费在线观看av的网站| 奇米影视一区二区三区| 国产精品亚洲片夜色在线| 在线免费看毛片| 国产一区福利在线| 粉嫩av免费一区二区三区| 欧美少妇bbw| 91免费在线看| 日韩高清国产精品| 色影视在线观看| 一区二区高清免费观看影视大全| 欧美一区二区激情| 亚洲同志男男gay1069网站| 色婷婷精品大在线视频 | 成人免费视频观看| 91精品蜜臀在线一区尤物| 韩国一区二区三区四区| 综合激情网...| 日韩成人久久久| 在线免费观看污视频| 欧美另类中文字幕| 亚洲第一天堂av| 欧美bbbbb性bbbbb视频| 日韩精品一区二区久久| 欧美伦理91i| 特级西西444www大精品视频免费看| 亚洲国产av一区二区三区| 国产日韩欧美三区| 国产精品你懂得| 国产www视频| 91亚洲资源网| 中文字幕久久综合| 18video性欧美19sex高清| 色94色欧美sute亚洲13| 久久久精品视频国产| 欧美一区 二区| 日韩在线视频中文字幕| 日本免费在线播放| 美女视频黄久久| 国产在线一区二区三区播放| www.黄在线观看| 亚洲午夜免费电影| 三级在线视频观看| caoporn成人免费视频在线| 亚洲午夜久久久影院| 久久久久久久久久久网| 日本午夜一区二区| 国产在线精品一区二区三区》| 日韩伦理在线观看| 欧美色视频日本版| 男人女人拔萝卜视频| 日韩av自拍| 日本精品视频在线观看| 亚洲精品97久久中文字幕| 中文字幕免费不卡| 国产91在线免费| 中文字幕日韩在线| 久久久国产成人精品| 91视频久久久| 99re成人在线| 日韩小视频网站| 日韩在线网址| 久久视频免费在线播放| 欧美特级黄色片| 99视频有精品| 精品国偷自产一区二区三区| 国产精品s色| 国产精品丝袜一区二区三区| 国产又爽又黄网站亚洲视频123| 亚洲男同1069视频| 自拍偷拍一区二区三区四区| 你懂的视频欧美| 欧美性资源免费| 天天干天天舔天天射| 一区二区三区精品视频在线| 亚洲一二三av| 欧美a级成人淫片免费看| 国产精品91在线观看| 欧美视频综合| 日韩欧美在线视频免费观看| a级一a一级在线观看| 在线欧美亚洲| 国产乱码精品一区二区三区不卡| 污片在线免费观看| 欧美成人精品二区三区99精品| 在线免费观看亚洲视频| 狠狠久久亚洲欧美| 吴梦梦av在线| 国产麻豆一区二区三区| 久久伊人精品视频| 国产三级精品在线观看| 亚洲欧美日韩中文播放 | 欧美黄色一区二区三区| 国产v综合v亚洲欧| 黄色大片中文字幕| 自拍自偷一区二区三区| 国产精品成人一区二区三区吃奶| www免费网站在线观看| 欧美精品在线一区二区| 青青草成人免费| av一区二区不卡| 成年网站在线免费观看| 久久99性xxx老妇胖精品| 国产成人精品综合| 在线观看麻豆蜜桃| 日韩一级片网址| 国产做受高潮漫动| 久久先锋影音av| 中文字幕成人在线视频| 中出一区二区| 欧美大陆一区二区| 黄色日韩网站| 欧美精品www| 国产最新视频在线观看| 欧美二区三区的天堂| 国产小视频在线看| 精品国产导航| 久久久久久欧美| 九九九伊在人线综合| 欧美色男人天堂| 麻豆影视在线播放| 久久精品一区八戒影视| 毛片毛片毛片毛| 亚洲日韩成人| 亚洲午夜精品久久久中文影院av| 日本一区影院| 国产成人亚洲综合青青| 91小视频xxxx网站在线| 精品视频—区二区三区免费| 艳妇乳肉豪妇荡乳av| 亚洲h精品动漫在线观看| 第一次破处视频| 国产成人免费在线视频| 超碰网在线观看| 欧美在线三级| 日韩欧美第二区在线观看| 日本精品在线播放| 国产精品91久久久久久| 91资源在线观看| 俺去啦;欧美日韩| 日本天堂影院在线视频| 91精品国产综合久久久久久久久久| 国产午夜精品无码一区二区| 国产精品免费视频网站| 成人在线视频免费播放| 韩国精品在线观看| 久久久久国产精品熟女影院| 在线日本成人| 国产性生活免费视频| 青青草91久久久久久久久| 国产一区二区三区高清| 国产一区二区三区免费观看在线| 国产成人短视频| eeuss鲁一区二区三区| 久久天天躁狠狠躁老女人| 国产一区电影| 亚洲欧美成人网| 亚洲欧美强伦一区二区| 91精品国产91久久综合桃花| 中文字幕 亚洲视频| 欧美性猛交xxxx免费看久久久| 久久久久成人精品无码| ...xxx性欧美| www.涩涩爱| 国产视频一区不卡| 亚洲av片不卡无码久久| 成人国产免费视频| 国产伦理在线观看| 国产老肥熟一区二区三区| 黄色手机在线视频| 热久久国产精品| 国产精品亚洲a| 久久久久99| www黄色av| 欧美一级久久| 红桃av在线播放| 久久久久久夜| 麻豆av免费在线| 天使萌一区二区三区免费观看| 97国产精东麻豆人妻电影 | 欧美视频一二区| 亚洲精品一区二区在线观看| 亚洲第一第二区| 精品国产自在久精品国产| 亚洲爆乳无码一区二区三区| 欧美成人福利视频| 亚洲欧美激情国产综合久久久| 欧美大胆人体bbbb| 亚洲AV无码国产精品午夜字幕| 日韩三级在线观看| 亚洲精品久久久蜜桃动漫 | 色天天综合久久久久综合片| 99精品在线播放| 在线精品亚洲一区二区不卡| 亚洲男人天堂网址| 欧美精品在线观看播放| 99久久精品国产一区二区成人| 日韩一卡二卡三卡| 国产91免费看| 亚洲午夜精品久久久久久性色| 国产黄色在线| 久久精品成人一区二区三区 | 亚洲综合精品久久| 日韩三级视频在线| 色呦呦网站一区| 国产免费不卡视频| 欧美精品一区二区三区蜜桃视频 | 中文字幕亚洲在| 欧美日韩精品在线观看视频 | 在线成人免费av| 成人午夜大片免费观看| 美国黄色a级片| 国产精品卡一卡二卡三| 欧美日韩免费一区二区| 欧美日韩在线一区| 中文字幕永久在线观看| 日韩一级片网站| 你懂的在线看| 久久视频在线视频| 一区二区乱码| 91在线播放国产| 露出调教综合另类| 亚洲免费视频一区| 国模吧视频一区| 一级黄色香蕉视频| 国产福利91精品| 一色道久久88加勒比一| 亚洲精品写真福利| 麻豆精品久久久久久久99蜜桃| 欧美精品在线观看播放| 五月婷婷激情在线| 精品国产一区二区三区久久狼黑人| 欧美性爽视频| 国产精品入口尤物| 黄色欧美在线| 福利网在线观看| 天堂成人国产精品一区| 蜜桃视频无码区在线观看| 国产三级精品视频| 国产一卡二卡在线播放| 欧美天堂亚洲电影院在线播放| 亚洲老妇色熟女老太| 一区二区亚洲精品国产| 电影在线观看一区| 亚洲一区美女视频在线观看免费| 久久93精品国产91久久综合| 男人添女人下部视频免费| 美女网站一区二区| www.自拍偷拍| 亚洲午夜久久久久| 国产毛片毛片毛片毛片毛片| 夜夜嗨av色综合久久久综合网| 国产盗摄一区二区| 91精品久久久久久| 精品99在线| 九色在线视频观看| 成人一区二区三区在线观看| 国产麻豆a毛片| 在线精品视频小说1| 四虎精品在永久在线观看| 欧美激情亚洲综合一区| 精品一区二区三区亚洲| 亚洲一区高清| 蜜臀av性久久久久蜜臀av麻豆| 国产又爽又黄无码无遮挡在线观看| 亚洲综合免费观看高清在线观看| 国产毛片久久久久| 久久久精品999| av激情成人网| 日韩欧美99| 日韩vs国产vs欧美| 国产aⅴ激情无码久久久无码| 福利视频导航一区| 亚洲欧美自偷自拍| 91超碰caoporn97人人| 成人av综合网| 国内精品在线观看视频| youjizz久久| 国产成人无码精品久在线观看| 亚洲成av人片在线观看香蕉| 欧美草逼视频| 国产一区二区三区免费不卡| 亚洲性色视频| 中文在线永久免费观看| 图片区小说区区亚洲影院| 免费观看的毛片| 午夜精品99久久免费| 日韩精品免费一区二区夜夜嗨| 欧美 日本 亚洲| 久久人人97超碰com| 无码人妻熟妇av又粗又大| 在线播放日韩专区| 伦一区二区三区中文字幕v亚洲| 一本一道久久a久久精品综合 | 日韩精品视频网站| 国产午夜福利一区| 欧美精品丝袜久久久中文字幕| 欧美激情黑人| 91香蕉视频在线下载| 国产综合婷婷| 老司机福利av| 欧美日韩高清一区二区不卡| www在线免费观看视频| 国产精品区一区二区三在线播放| 99国产精品久久久久久久| av黄色免费网站| 欧美狂野另类xxxxoooo| 欧美性video| 欧美亚洲另类在线一区二区三区| 日韩高清欧美激情| 1024手机在线视频| 精品视频久久久久久久| 激情亚洲小说| 久久精品xxx| 国产欧美va欧美不卡在线| 国产伦一区二区| 国产91ⅴ在线精品免费观看| 区一区二视频| 性活交片大全免费看| 色婷婷综合五月| 永久免费网站在线| 麻豆av一区二区| 国产真实乱偷精品视频免| 五月婷婷激情网| 日韩中文综合网| 老司机aⅴ在线精品导航 | 天堂久久一区| 日日摸日日碰夜夜爽无码| 中文字幕第一区第二区| 亚洲男人第一天堂| 国产精品入口免费视| 亚洲精品三级| 国精品无码一区二区三区| 精品视频—区二区三区免费| 亚洲精品影片| 日本xxxx黄色|