Corredor

ウェブ、プログラミングの勉強メモ。

elementFromPoint() という API があった

JavaScript 第6版

JavaScript 第6版

2018年の初記事です。今年も宜しくお願い致します。

さて、まだまだ DOM 操作には知らないことが沢山…。


document.elementFromPoint() という API があった。ページ左上からの X・Y 座標値を与えると、その位置にある要素を取得できるというモノだ。

検証用の CodePen を作った。

See the Pen Element From Point by Neos21 (@Neos21) on CodePen.

マウスカーソルを任意の要素に重ねると、その要素を取得して CSS セレクタ表現をポインタ付近に表示する。

作り方

予め CSS で html 要素と body 要素を画面いっぱいに広げておく。必須ではないが、コンテンツが少ないと body 要素の下の html 要素が見えてしまうので、body 要素を広げて置いておくと良いかと。

html,
body {
  margin: 0;
  padding: 0; /* 基本は margin だけで十分だけど */
  width: 100%;
  height: 100%;
}

サンプルページで作ったコードは以下のとおり。

document.addEventListener('DOMContentLoaded', (loadEvent) => {
  // ポインタ要素を生成しておく
  const pointer = document.createElement('div');
  // position: fixed を使うと完全にフローして画面外にはみ出てもスクロールバーが表示されたりしなくなる
  pointer.setAttribute('style', 'display: inline-block; position: fixed; top: -1000px; border: 1px solid #ccc; border-radius: 4px; padding: 10px; background: #fff; opacity: .7;');
  // スクロールバーは html 要素が出すので body 要素配下にいるポインタ要素はスクロールバーの下に隠れる
  document.body.appendChild(pointer);
  
  // 直前にポイントした要素を控えておく
  let previousElement;
  
  // 引数の要素の CSS セレクタ表現を作る
  const getQuerySelector = (elem) => {
    const tagName = elem.tagName.toLowerCase();
    let querySelector = tagName;
    if(elem.id.trim()) {
      querySelector += '#' + elem.id.trim();
    }
    if(elem.className.trim()) {
      querySelector += '.' + Array.apply(null, elem.classList).join('.');
    }
    return querySelector;
  };
  
  // ポインタの位置にある要素を取得し CSS セレクタ表現を表示する
  const getElementFromPoint = (event) => {
    // ポインタの位置を設定する
    pointer.style.top = (event.y + 10) + 'px';
    pointer.style.left = (event.x + 5) + 'px';
    
    // 指定の座標位置にある要素を取得する
    const elementFromPoint = document.elementFromPoint(event.x, event.y);
    // 要素がないか直前の要素と同じなら中止
    if(!elementFromPoint || elementFromPoint === previousElement) {
      return;
    }
    // 直前の要素として控えておく
    previousElement = elementFromPoint;
    
    // 当該要素の CSS セレクタ表現を作る
    let parentElement = elementFromPoint;
    let tagName = elementFromPoint.tagName.toLowerCase();
    let querySelector = getQuerySelector(elementFromPoint);
    // 親要素に遡って CSS セレクタ表現を作る
    while(tagName !== 'html') {
      parentElement = parentElement.parentElement;
      tagName = parentElement.tagName.toLowerCase();
      querySelector = getQuerySelector(parentElement) + ' > ' + querySelector;
    }
    
    // ポインタ要素に表示する
    pointer.textContent = querySelector;
  }
  
  document.addEventListener('mousemove', getElementFromPoint); // マウス操作中
  document.addEventListener('click'    , getElementFromPoint); // マウスボタン押下時
});

マウスカーソルに追随するポインタとか、選択した要素の CSS セレクタ表現を作る関数とか、余計な処理が混じっているので、肝心の箇所だけ抜き出してみる。

// クリックした座標位置の要素を取得する
document.addEventListener('click', (event) => {
  const clickedElement = document.elementFromPoint(event.x, event.y);
});

なんとコレだけ。

要素が絶対配置とかで重なっている時は、一番上に表示されている要素が取得できるようだ。

Dragula というドラッグ処理を扱えるライブラリで見かけた。