【PHP8.x】Dom\Element::closest()メソッドの使い方
closestメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
closestメソッドは、Dom\Elementクラスのインスタンスに対して、指定されたCSSセレクタに一致する最も近い祖先要素を検索するメソッドです。
このメソッドは、現在の要素自身は含まず、その親要素からHTMLやXML文書の階層構造(DOMツリー)をルート要素に向かって上位に遡りながら探索を行います。引数として、一つ以上の要素にマッチする有効なCSSセレクタ文字列(例えば、タグ名を示す'div'、クラス名を示す'.my-class'、IDを示す'#my-id'など)を一つ受け取ります。
検索の結果、指定されたセレクタに最初に一致する祖先要素が見つかった場合、その要素を表すDom\Elementオブジェクトを返します。もし、DOMツリーのルート要素に到達するまでに一致する祖先要素が一つも見つからなかった場合は、nullを返します。
この機能は、特にユーザーインターフェースにおけるイベント処理の場面で非常に便利です。例えば、リスト内の複数のボタンにイベントリスナーを設定する際、どのボタンがクリックされたかに関わらず、そのボタンを含む親のリストアイテム要素を特定したい場合に活用できます。子要素から直接目的の祖先要素を効率的に取得できるため、複雑なDOM構造を持つウェブページで特定の要素を簡単に特定し、コードの可読性と保守性を向上させるのに役立ちます。これにより、DOMの要素間を移動する複雑なロジックを簡潔に記述できます。
構文(syntax)
1<?php 2 3$dom = new DOMDocument(); 4$element = $dom->createElement('span'); // Dom\Element クラスのインスタンスとして扱えます 5 6$selector = 'div.parent'; // 検索したいCSSセレクター(文字列) 7 8$closestElement = $element->closest($selector); // Dom\Element インスタンスまたは null が返されます 9 10?>
引数(parameters)
string $selectors
- string $selectors: 指定されたCSSセレクタに一致する、現在の要素から最も近い祖先要素(または自分自身)を指定する文字列
戻り値(return)
?Dom\Element
指定された要素、もしくはそれより上位の要素で、指定されたCSSセレクターに一致する最初の親要素を返します。一致する要素が見つからない場合はnullを返します。
サンプルコード
Dom\Element::closest で祖先要素を検索する
1<?php 2 3/** 4 * 指定されたセレクタに一致する、最も近い祖先要素を返します。 5 * 6 * @param DOMElement $element 検索を開始する要素 7 * @param string $selectors CSS セレクタ文字列 8 * @return ?DOMElement 一致する最も近い祖先要素。見つからない場合は null。 9 */ 10function findClosest(DOMElement $element, string $selectors): ?DOMElement 11{ 12 $xpath = new DOMXPath($element->ownerDocument); 13 14 // セレクタを XPath 式に変換 15 $xpathQuery = "./ancestor::*[matches(name(), '" . str_replace(',', '|') . "')]"; 16 17 $elements = $xpath->query($xpathQuery, $element); 18 19 if ($elements && $elements->length > 0) { 20 return $elements->item(0); 21 } 22 23 return null; 24} 25 26// サンプル: HTML を作成して要素を検索 27$html = <<<HTML 28<!DOCTYPE html> 29<html> 30<body> 31 <div id="container"> 32 <div class="level1"> 33 <div class="level2"> 34 <p id="target">This is the target element.</p> 35 </div> 36 </div> 37 </div> 38</body> 39</html> 40HTML; 41 42$dom = new DOMDocument(); 43$dom->loadHTML($html); 44 45$targetElement = $dom->getElementById('target'); 46 47if ($targetElement) { 48 // 'level1' クラスを持つ最も近い祖先要素を検索 49 $closestElement = findClosest($targetElement, 'level1'); 50 51 if ($closestElement) { 52 echo "Closest element with class 'level1': " . $closestElement->getAttribute('class') . PHP_EOL; // 出力: Closest element with class 'level1': level1 53 } else { 54 echo "No element found." . PHP_EOL; 55 } 56 57 // 'container' id を持つ最も近い祖先要素を検索 58 $closestElement = findClosest($targetElement, 'container'); 59 60 if ($closestElement) { 61 echo "Closest element with id 'container': " . $closestElement->getAttribute('id') . PHP_EOL; // 出力: Closest element with id 'container': container 62 } else { 63 echo "No element found." . PHP_EOL; 64 } 65} 66?>
PHP 8 の Dom\Element クラスにおける closest メソッド(実際にはサンプルコードは自作関数 findClosest の例です)は、指定された CSS セレクタに一致する、要素の最も近い祖先要素を検索するために使用します。このメソッドは、システムエンジニアを目指す初心者の方にとって、DOM 構造内での要素の特定に役立ちます。
findClosest 関数は、検索を開始する DOMElement オブジェクトと、CSS セレクタ文字列を引数に取ります。セレクタ文字列は、検索する祖先要素の条件を指定します。関数は内部で DOMXPath を使用して、XPath クエリを実行し、セレクタに一致する祖先要素を検索します。
戻り値は、最初に一致した DOMElement オブジェクトです。一致する要素が見つからない場合は、null が返されます。
サンプルコードでは、HTML 文字列を DOMDocument オブジェクトにロードし、getElementById メソッドで特定の要素(id が "target" の要素)を取得しています。その後、findClosest 関数を使用して、level1 クラスを持つ最も近い祖先要素と、container id を持つ最も近い祖先要素を検索し、その結果をコンソールに出力しています。
この例では、findClosest 関数が、指定された要素から DOM ツリーを遡り、指定されたセレクタに合致する最も近い祖先要素を効率的に見つける方法を示しています。この手法は、複雑な HTML 構造内で特定の要素を特定し、操作する必要がある場合に非常に便利です。例えば、イベントが発生した要素の親要素の特定の属性を取得したり、特定のクラスを持つ親要素に基づいて処理を分岐させたりする際に利用できます。
DomElement::closestメソッド(PHP 8以降)の代替となるサンプルコードに関する注意点です。このコードは、XPathを使って祖先要素を検索しています。matches(name(), ...)関数を使用していますが、これは要素名を正規表現で比較します。セレクタ文字列をXPath式に変換する際に、,を|に置換していますが、セレクタが複雑な場合はXPath式が正しく生成されない可能性があります。属性セレクタなど、より複雑なCSSセレクタには対応していません。セキュリティ上の観点から、$selectors変数を直接XPathクエリに埋め込むのは、XPathインジェクションのリスクがあるため避けるべきです。代わりに、XPathのquery()メソッドでパラメータを使用することを検討してください。このコードは、PHP 8未満の環境や、より複雑なセレクタに対応する必要がある場合に参考になります。