之前寫過一篇只用 JavaScript 實作一鍵複製的作法,在了解原理後,想要在 Angular 中也來實作同樣的功能,其實也很簡單唷!
先提一下,複製、貼上這動作是由瀏覽器所提供的,所以需要使用到
document
和window
這個瀏覽器物件。
複製文字這件事,基本上三個動作:
- 找到目標區塊
- 選取目標
- 執行複製
找到目標區塊
使用 JavaScript 要找到 DOM 中的某段區塊,可使用 document.querySelector()
方法,搭配 CSS 選擇器來尋找目標區塊,這樣就可以取得所要的 DOM 節點內容。
在 Angular 中也可以使用上面的方法來尋找區塊,但更推薦的作法是使用 Angular 內部的 DOCUMENT
Token,透過注入的方式來使用 document
全域物件。
這樣的作法可以用在 AOT,或在寫測試時,可以用模擬的方式來取代全域物件。
// 1. 載入 DOCUMENT Token
import { DOCUMENT } from "@angular/platform-browser";
// ...
// 2. 使用 Token 注入至元件中
constructor( @Inject(DOCUMENT) private dom: Document) { }
// ...
// 3. 設計選取區塊的方法
selectText(selector: string): void {
const element = this.dom.querySelector(selector);
}
到目前為止,我們在 selectText()
裡面已經可以取到目標區塊並存在 element
裡面了。
由於
document
和window
都是瀏覽器提供的全域物件,但 Angular 只有提供DOCUMENT
Token 來實作注入,window
了話,則需要自己額外設計。為了簡化問題,這裡先直接用
window
全域物件來處理。
選取目標
具有 select() 方法的 DOM 元素
目前 DOM 元素中,只有 HTMLInputElement
和 HTMLTextAreaElement
有選取文字的方法 select()
,所以針對這兩種 DOM 元素滿容易處理的,直接使用該方法就可以選到目標文字。
其他 DOM 元素
但除上述兩者外,我們還希望能夠複製其他 DOM 元素的文字時,就需要透過 document.Range
物件和 window.getSelection()
方法,藉此來選取目標。
這邊稍微解釋一下 Range
和 Selection
這兩個物件的差別:
Range
物件所包含的是 DOM 裡面的元素,會包含節點及子節點的內容。Selection
物件則是從 Range 物件中,取得選取到的文字,如同使用者用滑鼠選取特定文字般,並記錄選取的起迄位置。
所以這兩者的運作環境是不一樣的,Range
在 DOM 裡面去選取區塊,而 Selection
比較像是瀏覽器功能列上的 Select all
功能,不過他不是全選,而是針對 Range
物件去找到選取目標。
如此一來 selectText()
改成如下:
// 3. 設計選取區塊的方法
selectText(selector: string): void {
const element = this.dom.querySelector(selector);
const isInputElement = element instanceof HTMLInputElement;
const isTextAreaElement = element instanceof HTMLTextAreaElement;
if (isInputElement || isTextAreaElement) {
(element as HTMLInputElement).select();
} else {
let range = this.dom.createRange();
range.selectNodeContents(element);
let selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}
執行複製
複製的行為是交由瀏覽器來處理,因此需要調用 document.execCommand()
方法,並告訴瀏覽器要執行 copy
這個動作。這裡的動作很簡單,一行就可以完成,另外再加上一些狀態訊息的輸出。
// 4. 執行瀏覽器的複製指令,複製選取到的文字
execCopy() {
try {
const copyStatus = this.dom.execCommand('copy');
const message = copyStatus ? 'copied' : 'failed';
console.log(message);
} catch (error) {
console.log(`${error}`);
}
window.getSelection().removeAllRanges();
}
這個方法最後調用了 removeAllRanges()
方法,這是取消選取的效果,讓使用者察覺不出來到底發生什麼事。
完整程式碼
參考資料: