Skip to content

Commit 38eacd9

Browse files
committed
added DomQuery::closest()
1 parent 3f1c668 commit 38eacd9

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

src/Framework/DomQuery.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,19 @@ public function matches(string $selector): bool
114114
}
115115

116116

117+
/**
118+
* Returns closest ancestor matching CSS selector.
119+
*/
120+
public function closest(string $selector): ?self
121+
{
122+
if (PHP_VERSION_ID < 80400) {
123+
throw new \LogicException('Requires PHP 8.4 or newer.');
124+
}
125+
$el = Dom\import_simplexml($this)->closest($selector);
126+
return $el ? simplexml_import_dom($el, self::class) : null;
127+
}
128+
129+
117130
/**
118131
* Converts a CSS selector into an XPath expression.
119132
*/

tests/Framework/DomQuery.fromHtml.84.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,24 @@ test('matches() checks if element matches selector', function () {
9797
Assert::false($para->matches('.test'));
9898
});
9999

100+
test('closest() finds nearest matching ancestor', function () {
101+
$dom = DomQuery::fromHtml('
102+
<div class="outer">
103+
<div class="middle">
104+
<div class="inner">
105+
<p>Test</p>
106+
</div>
107+
</div>
108+
</div>
109+
');
110+
111+
$p = $dom->find('p')[0];
112+
Assert::type(DomQuery::class, $p->closest('div'));
113+
Assert::true($p->closest('div')->matches('.inner'));
114+
Assert::true($p->closest('.outer')->matches('.outer'));
115+
Assert::null($p->closest('span'));
116+
});
117+
100118
test('find() returns empty array for no matches', function () {
101119
$dom = DomQuery::fromHtml('<div></div>');
102120
Assert::same([], $dom->find('nonexistent'));

0 commit comments

Comments
 (0)