iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
0
Modern Web

JS30 錄系列 第 22

Day 22 - Follow Along Link Highlighter

任務目標

製作一個會強調關鍵字的標記,當滑鼠移到某個關鍵字上頭時,標記會從畫面上追隨該關鍵字。範例連結

作法

要讓關鍵字標記真的有追隨過去的效果,只好創造出一個真正的獨立的元素做為該標記,並讓標記移動到希望他移動的位置。步驟如下:

  1. 創造標記
  2. 標記 CSS 要有 translate 屬性
  3. 選擇所有需要標記效果的關鍵字
  4. 讓關鍵字監聽 mouseenter 事件
  5. 若觸發監聽事件,回傳自訂函數內容如下
    1. 移動標記至關鍵字位置
    2. 將標記長寬與關鍵字長寬相同

首先是創造標記並賦予 CSS,程式碼如下:

const highlight = document.createElement('span');
highlight.classList.add('highlight')
document.body.append(highlight);

CSS 如下:

.highlight {
  /* 轉場的目的是為了讓標記在轉換關鍵字時有連續移動感 */
  transition: all 0.2s;
  border-bottom:2px solid white;
  position: absolute;
  top:0;
  background:white;
  left:0;
  z-index: -1;
  border-radius:20px;
  display: block;
  box-shadow: 0 0 10px rgba(0,0,0,0.2)
}

將標記事先放置在頁面上,但沒賦予長寬,所以呈現隱藏狀態。

接下來選擇所有關鍵字並監聽 mousenter 事件, 只要滑鼠移動到關鍵字上,便會觸發自訂函式 highlightLink 。程式碼如下:

const triggers = document.querySelectorAll('a');

function highlightLink() {
  // 標記關鍵字
}

triggers.forEach(a => a.addEventListener('mouseenter', highlightLink));

接下來實現自訂函式的內容,第一步必須讓標記寬高與關鍵字相等,但如何得知關鍵字的寬高呢?此時可以使用元素的 getBoundingClientRect() 方法,程式碼如下:

function highlightLink() {
  linkCoords = this.getBoundingClientRect();

  highlight.style.width = `${linkCoords.width}px`;
  highlight.style.height = `${linkCoords.height}px`;
}

getBoundingClientRect() 方法可以讓我們取得某個元素的長寬以及該元素相對於瀏覽器視窗原點的距離。這裡的 this 指向觸發 mouseenter 的元素。

寬和高分別存在該方法所回傳的物件的 widthheight 屬性。將其指定給標記,標記便會隨著滑鼠移到的該關鍵字寬高而改變。

我們也可以用一樣的方法取得關鍵字的位置並指定給標記,程式碼如下:

function highlightLink() {
  linkCoords = this.getBoundingClientRect();
  
  // 先把寬高、位置資料處理過
  const coords = {
    width: linkCoords.width,
    height: linkCoords.height,
    top: linkCoords.top + window.scrollY,
    left: linkCoords.left + window.scrollX,
  }

  highlight.style.width = `${coords.width}px`;
  highlight.style.height = `${coords.height}px`;
  highlight.style.transform = `translate(${coords.left}px, ${coords.top}px)`;
}

剛有提到由於 getBoundingClientRect() 取得的是相對於「瀏覽器視窗原點」的位置, top 指的是離瀏覽器視窗上方多遠, left 則是離瀏覽器視窗左方多遠,用這種方式來定位。像 Day 13 的示意圖一樣,我們要的是關鍵字在 document 內的位置,所以必須考慮瀏覽器視窗滾動的影響,將瀏覽器視窗的所在位置補償上去。

最後用 transform 屬性的 translate() 函數將標記移動到補償過的位置就大功告成了。

以上就是 JS30 第二十二篇!

原本這篇要分享 JS30 的 Geolocation API 的,但我發現某些資料只有在模擬器之中才會有效,在實體手機卻沒反應,只好等有研究出原因後再找機會和大家分享!

Reference

Element.getBoundingClientRect()
完整程式碼


上一篇
Day 21 - Speech Detection
下一篇
Day 23 - Speech Synthesis
系列文
JS30 錄30

尚未有邦友留言

立即登入留言