Удалите все дочерние узлы, но оставьте текстовое содержимое узла в javascript (без фреймворка)

Я пытаюсь удалить все дочерние элементы из узла, но оставить фактическое текстовое содержимое узла. т.е. исходить из этого:

<h3>
   MY TEXT
   <a href='...'>Link</a>
   <a href='...'>Link</a>
   <select>
      <option>Value</option>
      <option>Value</option>
   </select>
</h3>

к этому:

<h3>
   MY TEXT
</h3>

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

Этот:

var obj = document.getElementById("myID");
if ( obj.hasChildNodes() ){
    while ( obj.childNodes){
        obj.removeChild( obj.firstChild );       
    }
}

очевидно, приводит только к <h3></h3>, и когда я попытался:

var h3 = content_block.getElementsByTagName('h3')[0];
var h3_children = h3.getElementsByTagName('*');
for(var i=0;i<h3_children.length;i++){
   h3_children[i].parentNode.removeChild(h3_children[i]);
}

Он зависает на полпути. Я подумал, что у него возникли проблемы с удалением параметров, но изменение цикла for для пропуска удаления, если только h3_children[i].parentNode==h3 (т.е. только удаление дочерних элементов первого уровня) не останавливается после удаления первого элемента <a>.

Я уверен, что упускаю здесь что-то очень очевидное, но, возможно, пришло время обратиться к толпе. Как удалить все дочерние элементы, но оставить только textNodes первого уровня? И почему описанный выше подход не работает?

ПРАВКИ

Есть несколько рабочих решений, и это здорово, но я все еще немного озадачен тем, почему перебор и удаление h3.getElementsByTagName('*') не работает. Аналогичный подход(адаптировано из Blender) также не завершает процесс удаления дочерних узлов. Любые мысли о том, почему это будет?


person Ben D    schedule 19.10.2012    source источник
comment
Как ни странно, при этом удаляются все элементы <a>, кроме одного: jsfiddle.net/rSbmR.   -  person Blender    schedule 19.10.2012
comment
@Blender - Да, я получал аналогичные результаты с getElementsByTagName loop (удаление большинства, но не всех элементов). Есть мысли, почему?   -  person Ben D    schedule 19.10.2012


Ответы (5)


var h3=content_block.getElementsByTagName("h3")[0];
for(var i=0;i<h3.childNodes.length;i++)
{
  if(h3.childNodes[i].nodeType==3)//TEXT_NODE
  {
    continue;
  }
  else
  {
    h3.removeChild(h3.childNodes[i]);
    i--;
  }
}

демонстрация JSFiddle

Изменить:

Объединил i--, чтобы он выглядел короче:

var h3=content_block.getElementsByTagName("h3")[0];
for(var i=0;i<h3.childNodes.length;i++)
{
  if(h3.childNodes[i].nodeType==3)//TEXT_NODE
    continue;
  else
    h3.removeChild(h3.childNodes[i--]);
}

Изменить № 2:

На что указал @SomeGuy, сделайте его еще короче:

var h3=content_block.getElementsByTagName("h3")[0];
for(var i=0;i<h3.childNodes.length;i++)
{
  if(h3.childNodes[i].nodeType!=3)//not TEXT_NODE
    h3.removeChild(h3.childNodes[i--]);
}

Скобки тоже можно убрать, но это будет «менее читабельно» и «запутанно», поэтому я оставлю их там.

person Passerby    schedule 19.10.2012
comment
Хороший человек! Я подумал, что мне, вероятно, нужно запустить проверку для [x].nodeType==3, но я не подумал использовать h3.childNodes.length... Я продолжал пытаться понять, как использовать while ( obj.childNodes), не создавая бесконечный цикл. Есть идеи, почему getElementsByTagName() loop не работает? - person Ben D; 19.10.2012
comment
@BenD Обратите внимание на i-- в моем ответе? Всякий раз, когда вы удаляете дочерний узел, childNodes.length родительского узла сокращается на 1, поэтому вам также нужно отступить от своего итератора. В противном случае вы в конечном итоге вызовете h3_children[3], в то время как у него осталось только 2 дочерних элемента. - person Passerby; 19.10.2012
comment
Это может быть просто if (lala.nodeType != 3) remove, даже короче без бессмысленной ветки продолжения. - person Leonid; 19.10.2012
comment
@SomeGuy Спасибо! Мне нравится ваше решение, кстати. - person Passerby; 19.10.2012

Вы можете проверить свойства .nodeType или .nodeName для каждого узла.

Текстовые узлы имеют следующие свойства:

.nodeType == 3
.nodeName == '#text'`
person Rango    schedule 19.10.2012

Например:

var e = obj.firstChild
while (e) {
   if (e.nodeType == 3) {
      e = e.nextSibling
   } else {
      var n = e.nextSibling
      obj.removeChild(e)
      e = n
   }
}
person Leonid    schedule 19.10.2012

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

var h3 = document.getElementsByTagName('h3')[0];

if (h3.hasChildNodes()) {
    for (var i = h3.childNodes.length - 1; i >= 0; i--) {
        if (h3.childNodes[i].nodeName != "#text")
            h3.removeChild(h3.childNodes[i]);
    }
}

Надеюсь, это сработает.

person Md. Khaled Hussain    schedule 19.10.2012
comment
очень похоже на ответ Прохожего, но он работает, и его синтаксис немного короче - person Ben D; 19.10.2012

Что ж, вещь, которую я использовал (вдохновленная ответами здесь), выглядит примерно так:

   var h3 = document.getElementsByTagName("h3")[0];
    Array.protoype.filter.call(h3.childNodes, function(child){
        if (child.nodeType != 3) {
            h3.removeChild(child);
        }
    });
person cipher    schedule 06.11.2014