Почему IE11 обрабатывает узел.normalize () неправильно для символа минус?

У меня возникла проблема, когда текстовые узлы DOM с определенными символами ведут себя странно в IE при использовании узла.функция normalize () для объединения соседних текстовых узлов.

Я создал пример Codepen, который позволяет воспроизводить ошибку в IE11:http://codepen.io/anon/pen/BxoKH

вывод в IE11: '- Example'

вывод в Chrome и более ранних версиях IE:'Test - Example'

Как вы можете видеть, это усекает все до символа минус, который, по-видимому, рассматривается как разделяющий символ, по-видимому, из-за ошибки в собственной реализации normalize() в Internet Explorer 11 (но не IE10, или IE8, или даже IE6).

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

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

http://codepen.io/anon/pen/Bvgtb/ Это проверяет символы Юникода из 32-1000 и печатает те, которые не проходят тест (усекают данные при нормализации узлов) вы можете изменить его, чтобы проверить другие диапазоны символов, но будьте осторожны, чтобы увеличить диапазон слишком много в IE или он замерзнет.

Я создал отчет об ошибке IE и Microsoft отчеты, способные воспроизвести его на основе образца кода, который я предоставил. Проголосуйте за это, если вы также испытываете эту проблему: https://connect.microsoft.com/IE/feedback/details/832750/ie11-node-normalize-dom-implementation-truncates-data-when-adjacent-text-nodes-contain-a-minus-sign

5 ответов


другие ответы здесь несколько многословны и неполны - они не ходят по полному под-дереву DOM. Вот более комплексное решение:

function normalize (node) {
  if (!node) { return; }
  if (node.nodeType == 3) {
    while (node.nextSibling && node.nextSibling.nodeType == 3) {
      node.nodeValue += node.nextSibling.nodeValue;
      node.parentNode.removeChild(node.nextSibling);
    }
  } else {
    normalize(node.firstChild);
  }
  normalize(node.nextSibling);
}

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

вот codepen с моим обходным путем, который работает во всех браузерах:http://codepen.io/anon/pen/ouFJa

мое обходное решение было основано на некоторой полезной нормализации код я нашел здесь:https://stackoverflow.com/a/20440845/1504529 но был адаптирован к этой конкретной ошибке IE11, а не к той, которая обсуждалась этим сообщением:

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

function isNormalizeBuggy(){
  var testDiv = document.createElement('div');
  testDiv.appendChild(document.createTextNode('0-'));
  testDiv.appendChild(document.createTextNode('2'));
  testDiv.normalize();
  return testDiv.firstChild.length == 2;
}

function safeNormalize(DOMNode) {
  // If the normalize function doesn't have the bug relating to minuses,
  // we use the native normalize function. Otherwise we use our custom one.
  if(!isNormalizeBuggy()){
    el.normalize();
    return;
  }
  function getNextNode(node, ancestor, isOpenTag) {
    if (typeof isOpenTag === 'undefined') {
      isOpenTag = true;
    }
    var next;
    if (isOpenTag) {
      next = node.firstChild;
    }
    next = next || node.nextSibling;
    if (!next && node.parentNode && node.parentNode !== ancestor) {
      return getNextNode(node.parentNode, ancestor, false);
    }
    return next;
  }
  var adjTextNodes = [], nodes, node = el;
  while ((node = getNextNode(node, el))) {
    if (node.nodeType === 3 && node.previousSibling && node.previousSibling.nodeType === 3) {
      if (!nodes) {
        nodes = [node.previousSibling];
      }
      nodes.push(node);
    } else if (nodes) {
      adjTextNodes.push(nodes);
      nodes = null;
    }
  }

  adjTextNodes.forEach(function (nodes) {
    var first;
    nodes.forEach(function (node, i) {
      if (i > 0) {
        first.nodeValue += node.nodeValue;
        node.parentNode.removeChild(node);
      } else {
        first = node;
      }
    });
  });
};

Не точный ответ, но помогло в моем случае.

function safeNormalize(el) {
function recursiveNormalize(elem)
{
    for (var i = 0; i < elem.childNodes.length; i++) {
        if (elem.childNodes[i].nodeType != 3) {
            recursiveNormalize(elem.childNodes[i]);
        }
        else {
            if (elem.childNodes[i].nextSibling != null && elem.childNodes[i].nextSibling.nodeType == 3) {
                elem.childNodes[i].nodeValue = elem.childNodes[i].nodeValue + elem.childNodes[i].nextSibling.nodeValue;
                elem.removeChild(elem.childNodes[i].nextSibling);
                i--;
            }
        }
    }
}
recursiveNormalize(el);
}

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

Я думаю, что seprating двух функций делает для более чистого (и намного меньше) кода.

// textNode is a DOM text node
function collectTextNodes(textNode) {

  // while there are text siblings, concatenate them into the first   
  while (textNode.nextSibling) {
    var next = textNode.nextSibling;

    if (next.nodeType == 3) {
      textNode.nodeValue += next.nodeValue;
      textNode.parentNode.removeChild(next);

    // Stop if not a text node
    } else {
      return;
    }
  }
}

// element is a DOM element
function normalise(element) {
  var node = element.firstChild;

  // Traverse siblings, call normalise for elements and 
  // collectTextNodes for text nodes   
  while (node) {
    if (node.nodeType == 1) {
      normalise(node);

    } else if (node.nodeType == 3) {
      collectTextNodes(node);
    }
    node = node.nextSibling;
  }
}

function mergeTextNode(elem) {
    var node = elem.firstChild, text
    while (node) {
        var aaa = node.nextSibling
        if (node.nodeType === 3) {
            if (text) {
                text.nodeValue += node.nodeValue
                elem.removeChild(node)
            } else {
                text = node
            }
        } else {
            text = null
        }
        node = aaa
    }
}