1

我需要随时更改使用innerHTML在每个节点上设置的值。更改动态设置的innerHTML

我发现的最接近的解决方案是:

... 
Object.defineProperty(Element.prototype, 'innerHTML', { 
    set: function() { 
     // get value (ok) 
     var value = arguments[0]; 
     // change it (ok) 
     var new_value = my_function(value); 
     // set it (problem) 
     this.innerHTML = new_value;    // LOOP 
    } 
} 
... 

但很明显它是一个无限循环。 有没有办法调用原始的innerHTML集?

我也尝试Proxy way,但我不能让它工作。

更多细节:

我上使用反向代理生成并添加CSP政策,以一个网站一个实验项目,所以工作:

  • 网站的所有者会意识到这些“覆盖
  • 我需要处理产生的任何js代码客户端,它可能会引发 政策
  • 我需要在内容安全策略引擎评估之前对其进行修改! (这是一个需要这种“不那么好”解决的主要问题)

回答

3

强制性警告: 重写为Element.prototype任何属性的setter和getter势必会在任何产品级代码坏主意。如果您使用任何依赖于innerHTML的库来进行工作,或者项目中还有其他开发人员不知道这些更改,则情况可能会变得很奇怪。您也将失去在应用的其他部分“正常”使用innerHTML的能力。

也就是说,因为您还没有提供任何关于的信息,为什么您想要这样做,我只是假设您知道这些警告,而您仍然想要覆盖浏览器自己的功能,也许用于开发目的。


解决方案:您重写浏览器的原生引领者Element.prototype.innerHTML,但你也需要原来的二传手,以实现自己的目标。这可以使用Object.getOwnPropertyDescriptor完成,这是Object.defineProperty的“对应”。

(function() { 
 
    
 
    //Store the original "hidden" getter and setter functions from Element.prototype 
 
    //using Object.getOwnPropertyDescriptor 
 
    var originalSet = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML').set; 
 
    
 
    Object.defineProperty(Element.prototype, 'innerHTML', { 
 
    set: function (value) { 
 
     // change it (ok) 
 
     var new_value = my_function(value); 
 
     
 
     //Call the original setter 
 
     return originalSet.call(this, new_value); 
 
    } 
 
    }); 
 
         
 
    function my_function(value) { 
 
    //Do whatever you want here 
 
    return value + ' World!'; 
 
    } 
 
         
 
})(); 
 

 

 
//Test 
 
document.getElementById('test').innerHTML = 'Hello';
<div id="test"></div>

+0

没有必要定义一个新的getter,你可以让它使用旧的。 – Oriol

+0

@Oriol True,编辑答案。谢谢你的提示。 – noppa

+0

很好的答案!但是这种策略在Safari上不起作用,因为实现'innerHTML'是不可配置的。 –

1

有没有简单的方法以任意HTML字符串,要做到这一点,没有。

问题是您使用的是任意的HTML字符串。目前在元素上设置任意HTML的唯一方法是使用innerHTML。你必须找到一种不同的方式来设置一个元素上任意的HTML,例如追加HTML到一个临时节点,抓住它的内容:

// Attempt: build a temporary element, append the HTML to it, 
    // then grab the contents 
    var div = document.createElement('div'); 
    div.innerHTML = new_value; 
    var elements = div.childNodes; 

    for(var i = 0; i < elements.length; i++) { 
     this.appendChild(elements[ i ]); 
    } 

然而,这遭受了同样的问题,div.innerHTML = new_value;将永远因为递归您正在修改任意HTML设置的唯一入口点。

我能想到的唯一解决方案是实现一个真实的,完整的HTML解析器,可以采取任意HTML字符串,并将其转化为DOM的东西像document.createElement('p')等等,然后你可以添加到您当前的元素与appendChild节点。然而,这将是一个可怕的,过度工程化的解决方案。

所有这一切,你不应该这样做。这段代码会毁了某人的一天。这违反了我们在前端开发中欣赏的几个原则:

  • 不要修改默认对象原型。任何碰巧运行此代码,或者甚至在同一页面上运行代码(如第三方跟踪库)的人都会从他们的底下拉出地毯。追踪发生什么问题几乎是不可能的 - 没有人会想到寻找innerHTML劫持。
  • 安装人员通常用于计算属性或具有副作用的属性。你在劫持一个值并改变它。你面临着一个消毒问题 - 如果有人第二次设置了已被劫持的价值,会发生什么?
  • 不要编写棘手的代码。这段代码毫无疑问是一个“棘手”的解决方案。

最干净的解决方案可能只是在需要的地方使用my_function。这是可读的,短的,简单的,香草编程:

someElement.innerHTML = my_function(value); 

你可以或者定义的方法(因为它则会覆盖从用户的价值,我会做了财产法),如:

Element.prototype.setUpdatedHTML = function(html) { 
    this.innerHTML = my_function(html); 
} 

这当开发人员遇到这种情况时,它显然是非标准的,他们可以更轻松地寻找劫持Element原型的人。

+0

我同意你说的话,但是,由于在“详细信息”部分增加的原因,我不能定义一个新的方法(我通常不控制代码)和开发者会知道这些“_overwrites_”。 –