Source for file FluentDOM.php
Documentation is available at FluentDOM.php
* FluentDOM implements a jQuery like replacement for DOMNodeList
* @version $Id: FluentDOM.php 440 2010-04-16 17:21:08Z subjective $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @copyright Copyright (c) 2009 Bastian Feder, Thomas Weinert
* @tutorial FluentDOM.pkg
require_once(dirname(__FILE__ ). '/FluentDOM/Core.php');
* Include the handler helper class
require_once(dirname(__FILE__ ). '/FluentDOM/Handler.php');
* Function to create a new FluentDOM instance and loads data into it if
* a valid $source is provided.
* @param string $contentType optional, default value 'text/xml'
function FluentDOM($source = NULL, $contentType = 'text/xml') {
return $result->load($source, $contentType);
* FluentDOM implements a jQuery like replacement for DOMNodeList
* @method bool empty() Remove all child nodes from the set of matched elements.
* @method DOMDocument clone() Clone matched DOM Elements and select the clones.
* declaring an empty() or clone() method will crash the parser so we use some magic
* @param array $arguments
public function __call($name, $arguments) {
throw new BadMethodCallException('Unknown method '. get_class($this). '::'. $name);
* Execute a function within the context of every matched element.
* @param callback $function
public function each($function) {
foreach ($this->_array as $index => $node) {
* Retrieve the matched DOM elements in an array.
* Reduce the set of matched elements to a single element.
* @example eq.php Usage Example: FluentDOM::eq()
* @param integer $position Element index (start with 0)
public function eq($position) {
$result = $this->spawn();
if (isset ($this->_array[$position])) {
$result->push($this->_array[$position]);
* Removes all elements from the set of matched elements that do not match
* the specified expression(s).
* @example filter-expr.php Usage Example: FluentDOM::filter() with XPath expression
* @example filter-fn.php Usage Example: FluentDOM::filter() with Closure
* @param string|callback$expr XPath expression or callback function
public function filter($expr) {
$result = $this->spawn();
foreach ($this->_array as $index => $node) {
$check = $this->_test($expr, $node, $index);
* Retrieve the matched DOM elements in an array. A negative position will be counted from the end.
* @param integer|NULLoptional offset of a single element to get.
public function get($position = NULL) {
if (isset ($this->_array[$position])) {
return array($this->_array[$position]);
* Search for a given element from among the matched elements.
* @param NULL|string|DOMNode|DOMNodelist|Iterator$expr
public function index($expr = NULL) {
$targetNode = $this->_array[0];
$nodeList = $this->_match('preceding-sibling::node()', $targetNode);
foreach ($nodeList as $node) {
foreach ($this->_array as $index => $node) {
if ($this->_test($expr, $node)) {
foreach ($this->_array as $index => $node) {
if ($node->isSameNode($targetNode)) {
* Reduce the set of matched elements to those that have
* a descendant that matches the selector or DOM element.
* @param string|DOMNode$expr XPath expression or DOMNode
public function has($expr) {
$result = $this->spawn();
foreach ($this->_array as $node) {
if ($node instanceof DOMElement &&
$node->hasChildNodes()) {
foreach ($node->childNodes as $childNode) {
if ($expr instanceof DOMNode) {
if ($expr === $childNode) {
} elseif ($this->_test($expr, $childNode)) {
* Checks the current selection against an expression and returns true,
* if at least one element of the selection fits the given expression.
* @example is.php Usage Example: FluentDOM::is()
* @param string $expr XPath expression
public function is($expr) {
foreach ($this->_array as $node) {
return $this->_test($expr, $node);
* Translate a set of elements in the FluentDOM object into
* another set of values in an array (which may, or may not contain elements).
* If the callback function returns an array each element of the array will be added to the
* result array. All other variable types are put directly into the result array.
* @example map.php Usage Example: FluentDOM::map()
* @param callback $function
public function map($function) {
foreach ($this->_array as $index => $node) {
} elseif ($mapped instanceof DOMNodeList ||
$mapped instanceof Iterator ||
$mapped instanceof IteratorAggregate ||
foreach ($mapped as $element) {
* Removes elements matching the specified expression from the set of matched elements.
* @example not.php Usage Example: FluentDOM::not()
* @param string|callback$expr XPath expression or callback function
public function not($expr) {
$result = $this->spawn();
foreach ($this->_array as $index => $node) {
$check = $this->_test($expr, $node, $index);
* Selects a subset of the matched elements.
* @example slice.php Usage Example: FluentDOM::slice()
public function slice($start, $end = NULL) {
$result = $this->spawn();
} elseif ($end > $start) {
* Adds more elements, matched by the given expression, to the set of matched elements.
* @example add.php Usage Examples: FluentDOM::add()
* @param string $expr XPath expression
public function add($expr, $context = NULL) {
$result = $this->spawn();
if (!empty($targetNodes)) {
foreach ($targetNodes as $node) {
$result->push($this->_match($expr, $context));
$result->push($this->find($expr));
* Get a set of elements containing of the unique immediate
* childnodes including only elements (not textnodes) of each
* of the matched set of elements.
* @example children.php Usage Examples: FluentDOM::children()
* @param string $expr XPath expression
public function children($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$result->push($node->childNodes, TRUE);
foreach ($node->childNodes as $childNode) {
if ($this->_test($expr, $childNode)) {
$result->push($childNode, TRUE);
* Get a set of elements containing all of the unique immediate
* childnodes including elements and textnodes of each of the matched set of elements.
$result = $this->spawn();
foreach ($this->_array as $node) {
$result->push($node->childNodes, FALSE);
* Searches for descendent elements that match the specified expression.
* @example find.php Usage Example: FluentDOM::find()
* @param string $expr XPath expression
* @param boolean $useDocumentContext ignore current node list
public function find($expr, $useDocumentContext = FALSE) {
$result = $this->spawn();
if ($useDocumentContext ||
$result->push($this->_match($expr));
foreach ($this->_array as $contextNode) {
$result->push($this->_match($expr, $contextNode));
* Get a set of elements containing the unique next siblings of each of the
* @example next.php Usage Example: FluentDOM::next()
* @param string $expr XPath expression
public function next($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$next = $node->nextSibling;
while ($next instanceof DOMNode && !$this->_isNode($next)) {
$next = $next->nextSibling;
if (empty($expr) || $this->_test($expr, $next)) {
* Find all sibling elements after the current element.
* @example nextAll.php Usage Example: FluentDOM::nextAll()
* @param string $expr XPath expression
public function nextAll($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$next = $node->nextSibling;
while ($next instanceof DOMNode) {
if (empty($expr) || $this->_test($expr, $next)) {
$next = $next->nextSibling;
* Get all following siblings of each element up to but
* not including the element matched by the selector.
* @param string $expr XPath expression
$result = $this->spawn();
foreach ($this->_array as $node) {
$next = $node->nextSibling;
while ($next instanceof DOMNode) {
if (isset ($expr) && $this->_test($expr, $next)) {
$next = $next->nextSibling;
* Get a set of elements containing the unique parents of the matched set of elements.
* @example parent.php Usage Example: FluentDOM::parent()
$result = $this->spawn();
foreach ($this->_array as $node) {
if (isset ($node->parentNode)) {
$result->push($node->parentNode);
* Get the ancestors of each element in the current set of matched elements,
* optionally filtered by a selector.
* @example parents.php Usage Example: FluentDOM::parents()
* @param string $expr XPath expression
public function parents($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$parents = $this->_match('ancestor::*', $node);
for ($i = $parents->length - 1; $i >= 0; -- $i) {
$parentNode = $parents->item($i);
if (empty($expr) || $this->_test($expr, $parentNode)) {
$result->push($parentNode);
* Get the ancestors of each element in the current set of matched elements,
* up to but not including the element matched by the selector.
* @param string $expr XPath expression
$result = $this->spawn();
foreach ($this->_array as $node) {
$parents = $this->_match('ancestor::*', $node);
for ($i = $parents->length - 1; $i >= 0; -- $i) {
$parentNode = $parents->item($i);
if (!empty($expr) && $this->_test($expr, $parentNode)) {
$result->push($parentNode);
* Get a set of elements containing the unique previous siblings of each of the
* matched set of elements.
* @example prev.php Usage Example: FluentDOM::prev()
* @param string $expr XPath expression
public function prev($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$previous = $node->previousSibling;
while ($previous instanceof DOMNode && !$this->_isNode($previous)) {
$previous = $previous->previousSibling;
if (empty($expr) || $this->_test($expr, $previous)) {
$result->push($previous);
* Find all sibling elements in front of the current element.
* @example prevAll.php Usage Example: FluentDOM::prevAll()
* @param string $expr XPath expression
public function prevAll($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
$previous = $node->previousSibling;
while ($previous instanceof DOMNode) {
if (empty($expr) || $this->_test($expr, $previous)) {
$result->push($previous);
$previous = $previous->previousSibling;
* Get all preceding siblings of each element up to but not including
* the element matched by the selector.
* @param string $expr XPath expression
$result = $this->spawn();
foreach ($this->_array as $node) {
$previous = $node->previousSibling;
while ($previous instanceof DOMNode) {
if (isset ($expr) && $this->_isNode($previous)) {
if ($this->_test($expr, $previous)) {
$result->push($previous);
$previous = $previous->previousSibling;
* Get a set of elements containing all of the unique siblings of each of the
* matched set of elements.
* @example siblings.php Usage Example: FluentDOM::siblings()
* @param string $expr XPath expression
public function siblings($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
if (isset ($node->parentNode)) {
foreach ($node->parentNode->childNodes as $childNode) {
if (empty($expr) || $this->_test($expr, $childNode)) {
$result->push($childNode);
* Get a set of elements containing the closest parent element that matches the specified
* selector, the starting element included.
* @example closest.php Usage Example: FluentDOM::closest()
* @param string $expr XPath expression
public function closest($expr, $context = NULL) {
$result = $this->spawn();
foreach ($context as $node) {
if ($this->_test($expr, $node)) {
$node = $node->parentNode;
* Get a set of elements containing only the first of the currently selected elements.
public function first() {
* Get a set of elements containing only the last of the currently selected elements.
* Add the previous selection to the current selection.
$result = $this->spawn();
* Revert the most recent traversing operation,
* changing the set of matched elements to its previous state.
* Manipulation - Changing Contents
* Get or set the xml contents of the first matched element.
* @example xml.php Usage Example: FluentDOM::xml()
* @param string|Callback|Closure$xml XML fragment
* @return string|FluentDOM
public function xml($xml = NULL) {
foreach ($this->_array as $index => $node) {
if (!empty($xmlString)) {
foreach ($fragment as $contentNode) {
$node->appendChild($contentNode->cloneNode(TRUE));
foreach ($this->_array as $node) {
foreach ($fragment as $contentNode) {
$node->appendChild($contentNode->cloneNode(TRUE));
if (isset ($this->_array[0])) {
* Get the combined text contents of all matched elements or
* set the text contents of all matched elements.
* @example text.php Usage Example: FluentDOM::text()
* @param string|callback|Closure$text
* @return string|FluentDOM
public function text($text = NULL) {
foreach ($this->_array as $index => $node) {
$node->nodeValue = call_user_func($text, $node, $index, $node->nodeValue);
$node->nodeValue = $text;
foreach ($this->_array as $node) {
$result .= $node->textContent;
* Manipulation - Inserting Inside
* Append content to the inside of every matched element.
* @example append.php Usage Example: FluentDOM::append()
* @param string|array|DOMNode|Iterator$content DOMNode or DOMNodeList or xml fragment string
public function append($content) {
$result = $this->spawn();
$result->push($this->_document->appendChild($contentNode));
* Append all of the matched elements to another, specified, set of elements.
* Returns all of the inserted elements.
* @example appendTo.php Usage Example: FluentDOM::appendTo()
* @param string|array|DOMNode|DOMNodeList|FluentDOM$selector
$result = $this->spawn();
if (!empty($targetNodes)) {
* Prepend content to the inside of every matched element.
* @example prepend.php Usage Example: FluentDOM::prepend()
* @param string|array|DOMNode|Iterator$content
public function prepend($content) {
$result = $this->spawn();
* Prepend all of the matched elements to another, specified, set of elements.
* Returns all of the inserted elements.
* @example prependTo.php Usage Example: FluentDOM::prependTo()
* @param string|array|DOMNode|DOMNodeList|FluentDOM$selector
* @return FluentDOM list of all new elements
$result = $this->spawn();
if (!empty($targetNodes)) {
* Manipulation - Inserting Outside
* Insert content after each of the matched elements.
* @example after.php Usage Example: FluentDOM::after()
* @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content
public function after($content) {
$result = $this->spawn();
* Insert content before each of the matched elements.
* @example before.php Usage Example: FluentDOM::before()
* @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content
public function before($content) {
$result = $this->spawn();
* Insert all of the matched elements after another, specified, set of elements.
* @example insertAfter.php Usage Example: FluentDOM::insertAfter()
* @param string|array|DOMNode|DOMNodeList|Iterator$selector
$result = $this->spawn();
if (!empty($targetNodes)) {
* Insert all of the matched elements before another, specified, set of elements.
* @example insertBefore.php Usage Example: FluentDOM::insertBefore()
* @param string|array|DOMNode|DOMNodeList|Iterator$selector
$result = $this->spawn();
if (!empty($targetNodes)) {
* Manipulation - Inserting Around
* Wrap $content around a set of elements
* @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content
protected function _wrap($elements, $content) {
$isCallback = $this->_isCallback($content, FALSE, TRUE);
foreach ($elements as $index => $node) {
if (!empty($wrapContent)) {
if ($wrapperTemplate instanceof DOMElement) {
$wrapper = $wrapperTemplate->cloneNode(TRUE);
$targets = $this->_match('.//*[count(*) = 0]', $wrapper);
if ($simple || $targets->length == 0) {
$target = $targets->item(0);
if (isset ($node->parentNode)) {
$node->parentNode->insertBefore($wrapper, $node);
$target->appendChild($node);
* Wrap each matched element with the specified content.
* If $content contains several elements the first one is used
* @example wrap.php Usage Example: FluentDOM::wrap()
* @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content
public function wrap($content) {
$result = $this->spawn();
* Wrap al matched elements with the specified content
* If the matched elemetns are not siblings, wrap each group of siblings.
* @example wrapAll.php Usage Example: FluentDOM::wrapAll()
* @param string|array|DOMNode|Iterator$content
public function wrapAll($content) {
$result = $this->spawn();
//group elements by previous node - ignore whitespace text nodes
foreach ($this->_array as $node) {
$previous = $node->previousSibling;
while ($previous instanceof DOMText && $previous->isWhitespaceInElementContent()) {
$previous = $previous->previousSibling;
if ($previous !== $current) {
$groups[$counter][] = $node;
if (count($groups) > 0) {
foreach ($groups as $group) {
$wrapper = $wrapperTemplate->cloneNode(TRUE);
$targets = $this->_match('.//*[count(*) = 0]', $wrapper);
if ($simple || $targets->length == 0) {
$target = $targets->item(0);
if (isset ($node->parentNode)) {
$node->parentNode->insertBefore($wrapper, $node);
foreach ($group as $node) {
$target->appendChild($node);
* Wrap the inner child contents of each matched element
* (including text nodes) with an XML structure.
* @example wrapInner.php Usage Example: FluentDOM::wrapInner()
* @param string|array|DOMNode|DOMNodeList|Iterator$content
$result = $this->spawn();
foreach ($this->_array as $node) {
foreach ($node->childNodes as $childNode) {
$elements[] = $childNode;
$result->push($this->_wrap($elements, $content));
* Manipulation - Replacing
* Replaces all matched elements with the specified HTML or DOM elements.
* This returns the JQuery element that was just replaced,
* which has been removed from the DOM.
* @example replaceWith.php Usage Example: FluentDOM::replaceWith()
* @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content
* Replaces the elements matched by the specified selector with the matched elements.
* @example replaceAll.php Usage Example: FluentDOM::replaceAll()
* @param string|array|DOMNode|DOMNodeList|Iterator$selector
$result = $this->spawn();
if (!empty($targetNodes)) {
* Manipulation - Removing
* Remove all child nodes from the set of matched elements.
* This is the empty() method - but because empty
* is a reserved word we can no declare it directly
* @example empty.php Usage Example: FluentDOM:empty()
foreach ($this->_array as $node) {
if ($node instanceof DOMElement ||
$node instanceof DOMText) {
* Removes all matched elements from the DOM.
* @example remove.php Usage Example: FluentDOM::remove()
* @param string $expr XPath expression
* @return FluentDOM removed elements
public function remove($expr = NULL) {
$result = $this->spawn();
foreach ($this->_array as $node) {
if (isset ($node->parentNode)) {
if (empty($expr) || $this->_test($expr, $node)) {
$result->push($node->parentNode->removeChild($node));
* Manipulation - Creation
* Clone matched DOM Elements and select the clones.
* This is the clone() method - but because clone
* is a reserved word we can no declare it directly
* @example clone.php Usage Example: FluentDOM:clone()
$result = $this->spawn();
foreach ($this->_array as $node) {
$result->push($node->cloneNode(TRUE));
* Access a property on the first matched element or set the attribute(s) of all matched elements
* @example attr.php Usage Example: FluentDOM:attr() Read an attribute value.
* @param string|array$attribute attribute name or attribute list
* @param string|callback|Closure$value function callback($index, $value) or value
* @return string|FluentDOMattribute value or $this
public function attr($attribute, $value = NULL) {
//expr is an array of attributes and values - set on each element
foreach ($attribute as $key => $value) {
foreach ($this->_array as $node) {
if ($node instanceof DOMElement) {
$node->setAttribute($key, $value);
//empty value - read attribute from first element in list
if ($node instanceof DOMElement) {
return $node->getAttribute($attribute);
$value instanceof Closure) {
//value is function callback - execute it and set result on each element
foreach ($this->_array as $index => $node) {
if ($node instanceof DOMElement) {
call_user_func($value, $node, $index, $node->getAttribute($attribute))
// set attribute value of each element
foreach ($this->_array as $node) {
if ($node instanceof DOMElement) {
$node->setAttribute($attribute, (string) $value);
* Remove an attribute from each of the matched elements.
* @example removeAttr.php Usage Example: FluentDOM::removeAttr()
$attributes = array($name);
} elseif ($name !== '*') {
throw new InvalidArgumentException();
foreach ($this->_array as $node) {
if ($node instanceof DOMElement) {
for ($i = $node->attributes->length - 1; $i >= 0; $i-- ) {
$node->removeAttribute($node->attributes->item($i)->name);
foreach ($attributes as $attribute) {
if ($node->hasAttribute($attribute)) {
$node->removeAttribute($attribute);
* Adds the specified class(es) to each of the set of matched elements.
* @param string|callback|Closure$class
* Returns true if the specified class is present on at least one of the set of matched elements.
* @param string|callback|Closure$class
foreach ($this->_array as $node) {
if ($node instanceof DOMElement &&
$node->hasAttribute('class')) {
* Removes all or the specified class(es) from the set of matched elements.
* @param string|callback|Closure$class
* Adds the specified class if the switch is TRUE,
* removes the specified class if the switch is FALSE,
* toggles the specified class if the switch is NULL.
* @example toggleClass.php Usage Example: FluentDOM::toggleClass()
* @param string|callback|Closure$class
* @param NULL|boolean$switch toggle if NULL, add if TRUE, remove if FALSE
foreach ($this->_array as $index => $node) {
if ($node instanceof DOMElement) {
$class, $node, $index, $node->getAttribute('class')
if (empty($classString) && $switch == FALSE) {
if ($node->hasAttribute('class')) {
$node->removeAttribute('class');
if ($node->hasAttribute('class')) {
$currentClasses = array();
foreach ($toggledClasses as $toggledClass) {
if (isset ($currentClasses[$toggledClass])) {
if ($switch === FALSE || is_null($switch)) {
unset ($currentClasses[$toggledClass]);
if ($switch === TRUE || is_null($switch)) {
$currentClasses[$toggledClass] = TRUE;
if (empty($currentClasses)) {
$node->removeAttribute('class');
|