Как условно обернуть элементы с помощью DOM API? - PullRequest
0 голосов
/ 24 февраля 2019

Предположим, у нас есть этот вход:

<div wrap>1</div>
<div>2</div>
<div wrap>3</div>
<div wrap>4</div>
<div wrap>5</div>

Требуемый вывод должен быть:

<div class="wrapper">
  <div wrap>1</div>
</div>
<div>2</div>
<div class="wrapper">
  <div wrap>3</div>
  <div wrap>4</div>
  <div wrap>5</div>
</div>

Кроме того, предположим, что эти элементы являются прямыми дочерними элементами элемента body и могут быть другие несвязанные элементы или текстовые узлы до или после них.

Примечаниекак последовательные элементы группируются внутри одной обертки и не упаковываются по отдельности.

Как бы вы обработали DOMNodeList тела и вставили обертки в правильное место?

После разговора (комментариев) только об упаковкепрямые дочерние элементы элемента тела,

Для этого ввода:

<body>
  <div wrap>1
    <div wrap>1.1</div>
  </div>
  <div>2</div>
  <div wrap>3</div>
  <div wrap>4</div>
  <div wrap>5</div>
</body>

Требуемый вывод должен быть:

<body>
  <div class="wrapper">
    <div wrap>1
      <div wrap>1.1</div>
      <!–– ignored ––>.
    </div>
  </div>
  <div>2</div>
  <div class="wrapper">
    <div wrap>3</div>
    <div wrap>4</div>
    <div wrap>5</div>
  </div>
</body>

Обратите внимание, что элементы, которые не являются прямыми потомками элемента body, полностью игнорируются.

1 Ответ

0 голосов
/ 24 февраля 2019

Было интересно писать и было бы неплохо увидеть другие решения, но в любом случае это моя попытка.

Я добавил комментарии в код, а не описывал здесь метод, так как я думаю, что комментарии облегчают его понимание ...

// Test HTML
$startHTML = '<div wrap>1</div>
<div>2</div>
<div wrap>3</div>
<div wrap>4</div>
<div wrap>5</div>';

$doc = new DOMDocument();
$doc->loadHTML($startHTML);

$xp = new DOMXPath($doc);
// Find any div tag with a wrap attribute which doesn't have an immediately preceeding
// tag with a wrap attribute, (or the first node which means it won't have a preceeding
// element anyway)
$wrapList = $xp->query("//div[@wrap='' and preceding-sibling::*[1][not(@wrap)]
                           or position() = 1]");

// Iterate over each of the first in the list of wrapped nodes
foreach ( $wrapList as $wrap )  {
    // Create new wrapper 
    $wrapper = $doc->createElement("div");
    $class = $doc->createAttribute("class");
    $class->value = "wrapper";
    $wrapper->appendChild($class);

    // Copy subsequent wrap nodes (if any)
    $nextNode = $wrap->nextSibling;
    while ( $nextNode ) {
        $next = $nextNode;
        $nextNode = $nextNode->nextSibling;
        // If it's an element (and not a text node etc)
        if ( $next->nodeType == XML_ELEMENT_NODE ) {
            // If it also has a wrap attribute - copy it
            if ($next->hasAttribute("wrap") ) {
                $wrapper->appendChild($next);
            }
            // If no attribute, then finished copying
            else    {
                break;
            }
        }
    }
    // Replace first wrap node with new wrapper
    $wrap->parentNode->replaceChild($wrapper, $wrap);
    // Move the wrap node into the wrapper
    $wrapper->insertBefore($wrap, $wrapper->firstChild);
}
echo $doc->saveHTML();

Поскольку он использует HTML, конечный результатвсе также обернуто в стандартные тэги, но вывод (отформатированный) будет ...

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
    <body>
        <div class="wrapper">
            <div wrap>1</div>
        </div>
        <div>2</div>
        <div class="wrapper">
            <div wrap>3</div>
            <div wrap>4</div>
            <div wrap>5</div>
        </div>

    </body>
</html>

Редактировать:

Если вы хотите только применить егонаправить потомков тега <body>, а затем обновить выражение XPath, включив его в качестве критерия ...

$wrapList = $xp->query("//body/div[@wrap='' and preceding-sibling::*[1][not(@wrap)]
                       or position() = 1]");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...