Как удалить элемент html с помощью php? [Дубликат]

Вместо того, чтобы бросать код на вас, есть два понятия, которые являются ключом к пониманию того, как JS обрабатывает обратные вызовы и асинхронность. (это даже слово?)

Модель цикла события и параллелизма

Есть три вещи, о которых вам нужно знать; Очередь; цикл события и стек

. В широких упрощенных терминах цикл событий подобен диспетчеру проекта, он постоянно прослушивает любые функции, которые хотят запускать и взаимодействовать между очереди и стека.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Как только он получает сообщение для запуска чего-то, он добавляет его в очередь. Очередь - это список вещей, которые ждут выполнения (например, ваш запрос AJAX). Представьте себе это так:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Когда одно из этих сообщений будет исполнено, оно выталкивает сообщение из очереди и создает стек, стек - это все, что нужно выполнить JS для выполнения инструкции в сообщение. Таким образом, в нашем примере ему говорят позвонить foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

. Так что все, что foobarFunc должно выполнить (в нашем случае anotherFunction), будет вставлено в стек. исполняемый, а затем забытый - цикл события затем переместится на следующую вещь в очереди (или прослушивает сообщения)

. Главное здесь - порядок выполнения. Это

КОГДА что-то будет запущено

Когда вы совершаете вызов с использованием AJAX для внешней стороны или выполняете любой асинхронный код (например, setTimeout), Javascript зависит от ответ, прежде чем он сможет продолжить.

Большой вопрос, когда он получит ответ? Ответ в том, что мы не знаем, поэтому цикл событий ждет, когда это сообщение скажет: «Эй, забери меня». Если JS просто ждал этого сообщения синхронно, ваше приложение замерзнет, ​​и оно сосать. Таким образом, JS продолжает выполнение следующего элемента в очереди, ожидая, пока сообщение не будет добавлено обратно в очередь.

Вот почему с асинхронной функциональностью мы используем вещи, называемые обратными вызовами. Это похоже на обещание буквально. Как и в I , обещание что-то вернуть в какой-то момент jQuery использует специальные обратные вызовы, называемые deffered.done deffered.fail и deffered.always (среди других). Вы можете увидеть их все здесь

Итак, вам нужно передать функцию, которая в какой-то момент будет выполнена с переданными ей данными.

Поскольку обратный вызов не выполняется немедленно, но в более позднее время важно передать ссылку на функцию, которую она не выполнила. поэтому

function foo(bla) {
  console.log(bla)
}

, поэтому большую часть времени (но не всегда) вы пройдете foo не foo()

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

1
задан giorgio79 20 April 2015 в 19:53
поделиться

1 ответ

Причина, по которой удаляется только первый div, пожалуй, проще всего объясняется так:

Вы перебираете все дочерние узлы. Эта итерация начинается с установки текущего узла на первый ребенок ( DOMNode::$firstChild ). Затем вы обрабатываете этого дочернего элемента, а когда закончите, вы продолжаете следующий дочерний элемент (то есть DOMNode::$nextSibling ).

Но если теперь удалить текущий узел из родительского

$object->parentNode->removeChild($object);

У текущего узла в итерации больше нет следующего брака (поскольку он был удален из его родителя). Поэтому итерация foreach заканчивается сразу же после удаления первого элемента div .

Существуют различные способы решения этой проблемы. С чистым PHP и не используя любой xpath, вы можете сохранить все узлы для удаления в массиве сначала, а затем удалить их. Функция iterator_to_array весьма удобна в таких ситуациях:

$divs = iterator_to_array($doc->getElementsByTagName('div'));
foreach ($divs as $div) {
    $div->parentNode->removeChild($div);
}

Эти четыре строки кода заменяют всю логику итерации и рекурсии вашей (не работающей) функции ( !).

Вы также можете исправить свою функцию, используя CachingIterator , который уже внутренне уже является следующим элементом, в то время как вы итерации текущего (текущий элемент кэшируется). Он не будет признан недействительным, потому что, как только вы удалите текущий узел из родителя, следующий узел уже был извлечен.

Примерно для вашего кода, который изменил бы следующие строки:

foreach($object->childNodes as $child) {            
    iterate_children($child);
}

:

$children = $object->childNodes;
$children = new IteratorIterator($children);
$children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
foreach ($children as $child) {
    iterate_children($child);
}

, но обратите внимание, что этот код предназначен только для демонстрации. Если вы скопируете & amp; вставьте это в свой пример, это сработает, потому что у вас есть некоторые другие проблемы в вашем коде, которые стали бы серьезными с такими изменениями.

Этот код по-прежнему будет иметь рекурсию, которая на самом деле не нужна, поскольку вы могли бы выполнять итерацию узлы в порядке документа. Для этого у меня есть DOMNodeIterator в Iterator Garden . Эта библиотека также имеет простой DOMElementFilter в ветке разработки . Поскольку проблема с соседним братом здесь одинакова, использование этих двух требует также CachingITerator:

$divs = new CachingIterator(new DOMElementFilter(new DOMNodeIterator($doc), 'div'), CachingIterator::TOSTRING_USE_KEY);
foreach ($divs as $div) {
    $div->parentNode->removeChild($div);
}

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

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


По соображениям полноты здесь ваш код с лучшей логикой обработки ошибок и обхода:

function iterate_children(DOMNode $node)
{
    if ($node instanceof DOMElement and $node->tagName == "div") {
        $parent = $node->parentNode;
        $parent->removeChild($node);
        return;
    }

    $children = $node->childNodes;
    if (!$children) {
        return;
    }

    $children = new IteratorIterator($children);
    $children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
    foreach ($children as $child) {
        iterate_children_old($child);
    }
}

И здесь реализация без рекурсии и с массивом:

<?php
/**
 * PHPDom iterate through document and remove nodes without XPath
 */

/my totally random html
$html = '<p> Great <div> dont want this</div> </p><p> some more</p><div>more crap here</div>';

$doc          = new DOMDocument();
$doc->recover = true;
$saved        = libxml_use_internal_errors(true);
$doc->loadHTML($html);
libxml_use_internal_errors($saved);

$divs = iterator_to_array($doc->getElementsByTagName('div'));
foreach ($divs as $div) {
    $div->parentNode->removeChild($div);
}

echo $doc->saveHTML();
3
ответ дан hakre 26 August 2018 в 10:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: