Замените элементы без изображения в дереве DOM текстом в JS / jQuery - PullRequest
1 голос
/ 22 августа 2011

Я бы хотел заменить не img элементы внутри contenteditable их текстом. Однако я хочу сохранить все элементы img, в том числе вложенные в другие элементы. Другими словами:

С учетом ввода, такого как:

<div><span>Foo <strong>Bar <img src="blah.png"></strong> and more text <img src="another.png"></span> With some other text <img src="yetmore.png"></div>

Я хотел бы произвести:

Foo Bar <img src="blah.png"> and more text <img src="another.png"> With some other text <img src="yetmore.png">

Поскольку это contenteditable, я не хочу использовать innerHTML чтение / запись, так как это приведет к потере позиции курсора и т. П. (Восстановить его как Hard, потому что в итоге вы получите другое дерево DOM, так что Ваши узлы выбора теряются).

Лучше ли мне просто перебирать дерево, вручную разбивать и объединять текстовые узлы и т. Д.? Я надеюсь, что есть лучший способ или библиотека, которая уже может делать такие вещи ...

Ответы [ 3 ]

3 голосов
/ 22 августа 2011

Сделать неглубокий клон корневого элемента.Идите вниз по дереву элементов оригинала, собирая текст.Когда вы натолкнетесь на элемент img, добавьте собранный текст до текстового узла, который является потомком клона.Добавить изображениеНачните собирать текст снова.

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

Правка

Что-то вроде:

function toArray(o) {
  var a = [], i = o.length;
  while (i--) {
    a[i] = o[i];
  }
  return a;
}

function cleanUp(el) {

  var e = el.cloneNode(false);

  function addText(text) {
    if (text != '') {
      e.appendChild(document.createTextNode(text));
    }
  }

  function collectText(el) {
    var node, nodes = toArray(el.childNodes);
    var text = '';

    for (var i=0, iLen=nodes.length; i<iLen; i++) {
      node = nodes[i];

      if (node.tagName && node.tagName.toLowerCase() == 'img') {
        addText(text);
        e.appendChild(node);
        text = '';

      } else if (node.nodeType == 3) {
        text += node.data;

      } else if (node.nodeType == 1) {
        addText(text);
        text = '';
        collectText(node);
      }
    }

    if (text != '') {
      e.appendChild(document.createTextNode(text));
    }
  }
  collectText(el);
  el.parentNode.replaceChild(e, el);
}
3 голосов
/ 22 августа 2011

Выполнение замены DOM почти наверняка также приведет к потере позиции / выбора курсора, но это все еще правильный подход. Я бы порекомендовал Rangy для сохранения и восстановления выделения и кросс-браузерной обработки диапазона / выбора (раскрытие: я автор Rangy).

Вот пример, который удаляет не <img> элементы и сохраняет предыдущую позицию выделения / каретки во всех основных браузерах (включая IE 6). Он рекурсивно перемещает потомков <img> и текста основного узла контейнера в DocumentFragment и удаляет все остальные узлы по мере продвижения до окончательного добавления фрагмента в теперь пустой узел контейнера. Это также нормализует (то есть объединяет смежные текстовые узлы).

jsFiddle с выбором Rangy для сохранения и восстановления: http://jsfiddle.net/CRLRj/1/

Код удаления элемента:

function removeNonImgElements(node) {
    var frag = document.createDocumentFragment();

    function move(node, moveSelf) {
        var type = node.nodeType, name = node.nodeName;

        // Deal with child nodes first
        var child;
        while ( (child = node.firstChild) ) {
            move(child, true);
        }

        if (!moveSelf) {
            return;
        }

        // Keep text, images and Rangy selection marker elements
        if (type == 1 && (name == "IMG" ||
                 (name == "SPAN" && /^selectionBoundary/.test(node.id)))) {
            frag.appendChild(node);
        } else if (type == 3) {
            var previousNode = frag.lastChild;
            if (previousNode && previousNode.nodeType == 3) {
                // Concatenate text nodes rather than have two adjacent
                previousNode.data = previousNode.data + node.data;
                node.parentNode.removeChild(node);
            } else {
                frag.appendChild(node);
            }
        } else {
            node.parentNode.removeChild(node);
        }
    }

    move(node, false);
    node.appendChild(frag);
}
1 голос
/ 22 августа 2011
$(document).ready(function(){
    replaceChildren($('#contenteditable'));
});

function replaceChildren(elem){
    $(elem).children("*").not("img").each(function(){
        if ($(this).children("*").not("img").length>0){
            replaceChildren(this);
        }
        $(this).after($(this).html());
        $(this).remove();
    });
}

Какое-то рекурсивное решение.

...