This is a follow up question to Setter for HTMLInputElement.value.
If I changed the setter and getter of a single input-element (not on all input elements in general), and later on I want to make another change (for example done by a third party javascript-module) to this specific input element, how would the second changing work, so that it doesn't override the first change of getter and setter but would just add it sown change?
Specifically or in general: When changing the setter and getter how could the second changing javascript code establish what to change and how to change it without losing the previous addage?
Edit: After the following code, what if I (or a third party module) wants to "inject" a second, a "doSomeLogicWithInput2"-function, into exactly the same input-setter (or getter) with the id "anInputElement"?
function doSomeLogicWithInput(input) {
if (input.value.toLowerCase()==='one') {input.style.color='black'; } else { input.style.color='red';}
}
var inputElement = document.getElementById('anInputElement');
Object.defineProperty(inputElement, 'value', {
// HTMLInputElement is like inputElement.prototype.Symbol ???
set: function(newValue) {
var valueProp = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
var valuePropResult = valueProp.set.call(inputElement, newValue);
doSomeLogicWithInput(inputElement);
return valuePropResult;
},
get : function() {
var valueProp = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
return valueProp.get.call(inputElement);
}
});
// Catch end-user changes
inputElement.addEventListener('input', function(event) { doSomeLogicWithInput(event.target); }, true);
Edit 2:
This is an example how I tried to use Bergi's functions, but it doesn't work really. In the function logic el.value
returns 'undefined'
.
<html><head>
<script>
function getAccessorDescriptor(obj, prop) {
if (obj == null) { // only necessary if you may overwrite missing properties
return {
get(){},
set(){},
enumerable: false,
configurable: true
};
}
const desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc) {
return getAccessorDescriptor(Object.getPrototypeOf(obj), prop);
}
if ("writable" in desc) { // only necessary if you might overwrite data properties
return {
get() { return desc.value; },
set(v) { if (desc.writable) desc.value = v; else throw new TypeError() },
enumerable: desc.enumerable,
configurable: true,
}
}
return desc;
}
function addSetLogic(obj, prop, logic) {
const oldDescriptor = getAccessorDescriptor();
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Object.defineProperty(obj, prop, {
...oldDescriptor,
set(value) {
oldDescriptor.set.call(this, value);
// ^^^^^^^^^^^^^^^^^
logic(this, value);
}
});
}
var elem;
function runtest() {
elem = document.getElementById('mycolor');
addSetLogic(elem, 'value', logic);
elem.value='bar';
}
function logic(el,newValue) {
console.log(el,el.value); // <- el.value is undefined
if (el.value==='standard') { el.style.color='black;'}
else { el.style.color='red'; }
}
</script>
</head>
<body onload="runtest();">
<input id="mycolor" type="text" value="foo">
</body>
</html>
Don't get the Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')
every time you access the property, and also don't get it from HTMLInputElement.prototype
but rather from the inputElement
itself if there already is one. Rather use the one you have replaced/overwitten with the new descriptor:
function getAccessorDescriptor(obj, prop) {
if (obj === null) { // only necessary if you may overwrite missing properties
return {
get(){},
set(){},
enumerable: false,
configurable: true
};
}
const desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc) {
return getAccessorDescriptor(Object.getPrototypeOf(obj), prop);
}
if ("writable" in desc) { // only necessary if you might overwrite data properties
return {
get() { return desc.value; },
set(v) { if (desc.writable) desc.value = v; else throw new TypeError() },
enumerable: desc.enumerable,
configurable: true,
}
}
return desc;
}
function addSetLogic(obj, prop, logic) {
const oldDescriptor = getAccessorDescriptor(obj, prop);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Object.defineProperty(obj, prop, {
...oldDescriptor,
set(newValue) {
oldDescriptor.set.call(this, newValue);
// ^^^^^^^^^^^^^^^^^
logic(this, newValue);
}
});
}
Then you can do
addSetLogic(inputElement, 'value', doSomeLogicWithInput);
addSetLogic(inputElement, 'value', doSomeLogicWithInput2);
as often as you want.