2

Without explaining the details of the project I'm working on in too much detail, I am trying to determine a way in which I can either implement an event listener for when document.getElementById().value is accessed, or override it in a similar way to an object, and define custom behaviour for the getting and setting of this value. Is this possible?

I've tried the following, to no avail:

Object.defineProperty(document.getElementById, 'nodeValue', {
    get: function () {
        console.log('document.getElementById value accessed');
        return this.nodeValue;
    },
    set: function (value) {
        console.log('document.getElementById value set');
        this.nodeValue = value;
    }
});

and the same as above but with value instead of nodeValue:

Object.defineProperty(document.getElementById, 'value', {
    get: function () {
        console.log('document.getElementById value accessed');
        return this.value;
    },
    set: function (value) {
        console.log('document.getElementById value set');
        this.value = value;
    }
});

Apologies if this seems a somewhat farfetched approach, the intricacies of JavaScript behind-the-scenes isn't something I am too familiar with. The above code does show what I am trying to achieve, however. I just don't know how!

I've spent some time on MDN trying to understand just how this works and from what I can gather, getElementById() returns an Element which inherits from the Node interface which contains a nodeValue, and I this that this nodeValue is what I am interested in, but I cannot be certain.

Edit: I'm looking for this behaviour to be generic, I have more than one (but an unknown number of) elements, so I'm not trying to apply this to a specific element.

PsychoMantis
  • 993
  • 2
  • 13
  • 29
  • 1
    You should not monkeypatch DOM elements, have you tried [`input`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) event?. Or perhaps [Custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) is more suitable for your needs? – Teemu May 01 '22 at 09:09
  • You can [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) the element without altering the input internals. – pilchard May 01 '22 at 09:15
  • You want to capture the `value` property of the *result of calling* `getElementById`, not of the function itself, right? – user3840170 May 01 '22 at 09:27
  • Sorry guys I should've clarified, I don't have just one element, I'm trying to *know* when an element value is changed or accessed via code. The `input` event doesn't tell me that, and custom elements are out of the question because of the use case, the use case primarily being large forms with multiple inputs. The Proxy object does look like a possibility though, I'll look into that! – PsychoMantis May 01 '22 at 09:28
  • 2
    In fact there is a section on Proxying DOM elements in the docs: [Proxy: Manipulating DOM nodes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#manipulating_dom_nodes) (including updating other inputs on change/access) – pilchard May 01 '22 at 09:28

1 Answers1

2

First, modifying Element object is a bad idea. Extending it, is bad, but not as bad as modifying it. This is because, JavaScript implementation on web browser does not put details on how the underlying API works. For example,

1. Modify Element Object

We have a document as follows:

<div>
  <h1 id="elem-id">Header Content</h1>
</div>

we could get its id by calling these instructions

let elem = document.getElementById('elem-id');
elem.getAttribute('id'); // 'elem-id';

We can modify getAttribute() by using:

Element.prototype.getAttribute = function (attributeKey) {
// Implement your own function here
return 'ok';
}

Next, when you call elem.getAttribute('id') it should return ok.

This is bad because there is no way to revert back to default implementation.

2. Extending Element Object

Next less bad stuff is, extending Object. We could simply do:

Element.prototype.getAttributeAndFireEvent = function (attributeKey) {
  console.log(`getting attribute for ${attributeKey}`); // Could be an eventEmitter
  return this.getAttribute(attributeKey);
}

then, we could use these methods by calling it like:

elem.getAttributeAndFireEvent('elem-id');

This will work but please be careful in modifying global object, it may cause unintended effect.

References:

  1. In Javascript, can you extend the DOM?
Raynal Gobel
  • 991
  • 15
  • 25
  • 1
    Thank you for your time and information, I appreciate it. I ended up creating a list and extending `getElementById()` to throw a warning instead, seemed like the nicer choice =) – PsychoMantis May 01 '22 at 10:14