为什么IE11对Node.normalize()的负号处理不正确?

Why Does IE11 Handle Node.normalize() Incorrectly for the Minus Symbol?

本文关键字:处理 不正确 IE11 Node normalize 为什么      更新时间:2023-09-26

我一直遇到一个问题,当使用Node.normalize()函数连接相邻的文本节点时,具有某些字符的DOM文本节点在IE中表现奇怪。

我已经创建了一个Codepen示例,允许您在IE11中重现错误:http://codepen.io/anon/pen/BxoKH

IE11输出:'-示例'

Chrome和早期版本IE的输出:'Test - Example'

正如你所看到的,这截断了减号之前的所有内容,减号显然被视为分隔字符,显然是由于Internet Explorer 11(但不是IE10,或IE8,甚至IE6)中normalize()的本机实现中的错误。

有没有人能解释为什么会发生这种情况,有没有人知道导致这个问题的其他字符序列?

Edit -我写了一个代码依赖程序,它将测试Unicode字符的部分,以识别导致这种行为的字符。它影响的字符似乎比我最初意识到的要多得多:

http://codepen.io/anon/pen/Bvgtb/这将测试32-1000之间的Unicode字符,并打印那些未通过测试的字符(当节点被规范化时截断数据)。您可以修改它来测试其他范围的字符,但要注意在IE中增加太多的范围,否则它会冻结。

我已经创建了一个IE错误报告和微软报告,能够根据我提供的代码样本重现它。如果你也遇到了这个问题,请投票:https://connect.microsoft.com/IE/feedback/details/832750/ie11-node-normalize-dom-implementation-truncates-data-when-adjacent-text-nodes-contain-a-minus-sign

这里的其他答案有些冗长和不完整—它们没有遍历整个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);
}

我通过简单地在JS中重新实现normalize方法创建了一个解决方案,但为此挣扎了许多小时,所以我想我应该做一个so帖子来帮助其他人,并希望得到更多的信息来帮助满足我对这个浪费了我大部分时间的bug的好奇心,哈哈。

下面是我的解决方案的代码依赖,适用于所有浏览器: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);
}

规范化代码看起来有点复杂,下面的代码更简单一些。它遍历要规范化的节点的兄弟节点,收集文本节点,直到遇到一个元素。然后它调用自身并收集该元素的文本节点,依此类推。

我认为将这两个函数分开会使代码更简洁(和更少)。

// 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
    }
}