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

Javascript 为什么IE11不正确地处理负号的Node.normalize()?,javascript,html,internet-explorer,dom,Javascript,Html,Internet Explorer,Dom,我遇到过这样一个问题:当使用Node.normalize()函数连接相邻的文本节点时,IE中带有某些字符的DOM文本节点的行为异常 我创建了一个Codepen示例,它允许您在IE11中重现错误: IE11中的输出:'-Example' Chrome和IE早期版本的输出:“测试示例” 如您所见,这会截断减号之前的所有内容,减号显然被视为定界字符,这显然是由于Internet Explorer 11(但不是IE10、IE8,甚至IE6)中normalize()的本机实现中存在错误所致 有人能解释为什

我遇到过这样一个问题:当使用Node.normalize()函数连接相邻的文本节点时,IE中带有某些字符的DOM文本节点的行为异常

我创建了一个Codepen示例,它允许您在IE11中重现错误:

IE11中的输出:'-Example'

Chrome和IE早期版本的输出:“测试示例”

如您所见,这会截断减号之前的所有内容,减号显然被视为定界字符,这显然是由于Internet Explorer 11(但不是IE10、IE8,甚至IE6)中normalize()的本机实现中存在错误所致

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

编辑-我编写了一个代码笔,它将测试Unicode字符的各个部分,以识别导致这种行为的字符。它似乎影响了比我最初意识到的更多的角色:

这将测试32-1000之间的Unicode字符,并打印未通过测试的字符(在节点规范化时截断数据)。您可以修改它以测试其他字符范围,但请小心在IE中过度增加范围,否则会冻结

我已经创建了一个IE bug报告,Microsoft报告能够根据我提供的代码示例重现它。如果您也遇到此问题,请投票:

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

这是一个代码笔,我的解决方案适用于所有浏览器:

我的变通方法是基于我在这里找到的一些有用的规范化代码:但它是针对这个特定的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);
}
函数安全规格化(el){
函数递归规格化(elem)
{
for(var i=0;i
这里的其他答案有些冗长和不完整-它们没有遍历完整的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);
}

什么版本的IE?我在IE8和IE10上都得到了预期的结果,不需要解决方法。对不起,IE11,我现在正在测试更多的浏览器版本。事实上,它甚至在IE6中也没有解决方法:什么bug,确切地说?哇,但它在IE11中失败了。实际上,这有一个bug…IE11中的normalize被破坏了,这实际上没有正确地填充功能-您正在从DOM中删除节点,如果您在其他地方引用了要删除的DOM节点,它就不能正常工作#normalize还将合并您的DOM节点引用指针。@Dan请解释“如果您在某处有对要删除的DOM节点的引用…#normalize还将合并您的DOM节点引用指针”。在IE中,我看不到对“#规范化”文本节点的引用仍然引用已删除的节点和
节点。parentNode
null
。或者您正在谈论?当节点只有一个子节点时,normalize中的while部分不起作用。这实际上没有正确地填充功能-您正在从DOM中删除节点,如果您在其他地方引用了要删除的DOM节点,则该部分不起作用#normalize将合并您的DOM节点引用指针以及更多的代码,是的,但是所有的答案都会遍历传递的节点下面的整个DOM。我喜欢这个解决方案。我遇到了一个大问题:如果您有一个大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);
}
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
    }
}