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_」。 –