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 обязан быть плохой идеей в любом коде производства уровня. Если вы используете любые библиотеки, которые полагаются на 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

Нет необходимости определять новый геттер, вы можете просто позволить ему использовать старый. – 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); 
} 

Этом когда разработчик столкнется с setUpdatedHTML, он будет явно нестандартным, и они смогут искать кого-то, угоняющего прототип элемента легче.

+0

Я согласен с тем, что вы говорите, но, из-за причинами, добавляемых в разделе «подробнее», я не могу определить новый метод (я часто не контролирую этот код) и разработчик будут знать об этих «_www». –

Смежные вопросы