7

I have seen a lot of discussion regarding extending Element. As far as I can tell, these are the main issues:

  • It may conflict with other libraries,
  • It adds undocumented features to DOM routines,
  • It doesn’t work with legacy IE, and
  • It may conflict with future changes.

Given a project which references no other libraries, documents changes, and doesn’t give a damn for historical browsers:

Is there any technical reason not to extend the Element prototype. Here is an example of how this is useful:

Element.prototype.toggleAttribute=function(attribute,value) {
    if(value===undefined) value=true;
    if(this.hasAttribute(attribute)) this.removeAttribute(attribute);
    else this.addAttribute(attribute,value);
};

I’ve seen too many comments about the evils of extending prototypes without offering a reasonable explanation.

Note 1: The above example is possibly too obvious, as toggleAttribute is the sort of method which might be added in the future. For discussion, imagine that it’s called manngoToggleAttribute.

Note 2: I have removed a test for whether the method already exists. Even if such a method already exists, it is more predictable to override it. In any case, I am assuming that the point here is that the method has not yet been defined, let alone implemented. That is the point here.

Note 3: I see that there is now a standard method called toggleAttribute which doesn’t behave exactly the same. With modification, the above would be a simple polyfill. This doesn’t change the point of the question.

SecretAgentMan
  • 2,856
  • 7
  • 21
  • 41
Manngo
  • 14,066
  • 10
  • 88
  • 110
  • 4
    You may also have an issue with forward compatibility - the function you are adding may become part of the standard in the future. – TimoStaudinger Jul 15 '16 at 01:46
  • 1
    What exactly do you mean by "technical reason"? No, extending `Element` does (as you figured out) work fine in controlled enviroments. – Bergi Jul 15 '16 at 01:46
  • 1
    @Manngo: And with that test you have actually broken forward compatibility. The point is that the future standard might do something different than your custom method. – Bergi Jul 15 '16 at 01:49
  • @Vohuman I’ve already read that article. It’s very old and most of the issues are no longer relevant. In fact they’re mostly summarised in my points above. – Manngo Jul 15 '16 at 01:55
  • Side note: `this[(this.hasAttribute(attribute) ? 'remove' : 'add')+'Attribute'](attribute, value !== false)` is shorter – Washington Guedes Jul 15 '16 at 01:55
  • @Guedes Nice. I might use that in real life, but not for my introductory students. Thanks – Manngo Jul 15 '16 at 01:57
  • One _possible_ problem is the condition `if(!Element.prototype.toggleAttribute)`, because if the condition become false in the future the behavior may be different from your function. IMO just drop the condition off and do your function return some value. – Washington Guedes Jul 15 '16 at 02:02
  • As others have mentioned, it may become part of the Element API spec in the future and if your application relies on it, it may have different behavior. That's why it's recommended to only extend the prototype with polyfills of supported features since the behavior is already defined. – skyline3000 Jul 15 '16 at 02:08
  • Or add your initials in uppercase before the function name ;) – Washington Guedes Jul 15 '16 at 02:21
  • @Guedes So what? A toggle is a toggle, unless you change the definition of a toggle there can be no other behavior than toggling the attribute/property. And either way, while browser code may be written "in the stone" - your code isn't! – Bekim Bacaj Jul 15 '16 at 02:28
  • @BekimBacaj. If me I would just remove the if condition. I'm not against adding functions to Element's prototype – Washington Guedes Jul 15 '16 at 02:44
  • It seems that future compatibility appears to be the major issue here … ? – Manngo Jul 15 '16 at 02:57
  • @Manngo: It's overall compatibility with anything that might be different in an environment unlike the one you're currently testing in. Extending natives just makes your code more brittle (i.e. more likely to fail under unexpected circumstances). Per your question, you've already ruled out the past, third party code and unsupported browsers; you can hardly (or will not want to) rule out the future. – Bergi Jul 15 '16 at 03:05

5 Answers5

6

Is it ok? Technically yes. Should you extend native APIs? As a rule of thumb no. Unfortunately the answer is more complex. If you are writing a large framework like Ember or Angular it may be a good idea to do so because your consumers will have Benifits if better API convenience. But if you're only doing this for yourself then the rule of thumb is no.

The reasoning is that doing so destabilizes the trust of that object. In other words by adding, changing, modifying a native object it no longer follows the well understood and documented behavior that anyone else (including your future self) will expect.

This style hides implementation that can go unnoticed. What is this new method?, Is it a secret browser thing?, what does it do?, Found a bug do I report this to Google or Microsoft now?. A bit exaggerated but the point is that the truth of an API has now changed and it is unexpected in this one off case. It makes maintainability need extra thought and understanding that would not be so if you just used your own function or wrapper object. It also makes changes harder.

Relevant post: Extending builtin natives. Evil or not?

Instead of trying to muck someone else's (or standard) code just use your own.

function toggleAttribute(el, attribute, value) {
  var _value = (value == null ? true : value;
  if (el.hasAttribute(attribute)) {
    el.removeAttribute(attribute);
  } else {
    el.addAttribute(attribute, _value);
  }
};

Now it is safe, composible, portable, and maintainable. Plus other developers (including your future self) won't scratch their heads confused where this magical method that is not documented in any standard or JS API came from.

Sukima
  • 9,965
  • 3
  • 46
  • 60
  • OP asked if and why he shouldn't extend `Element` prototype. He did not ask for a solution that does not extend it. – pishpish Jul 16 '16 at 12:03
3

Do not modify objects you don't own.

Imagine a future standard defines Element.prototype.toggleAttribute. Your code checks if it has a truthy value before assigning your function. So you could end up with the future native function, which may behave differently than what you expected.

Even more, just reading Element.prototype.toggleAttribute might call a getter, which could run some code with undesired sideways effects. For example, see what happens when you get Element.prototype.id.

You could skip the check and assign your function directly. But that could run a setter, with some undesired sideways effects, and your function wouldn't be assigned as the property.

You could use a property definition instead of a property assignment. That should be safer... unless Element.prototype has some special [[DefineOwnProperty]] internal method (e.g. is a proxy).

It might fail in lots of ways. Don't do this.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • so what if it does? – Bekim Bacaj Jul 15 '16 at 02:30
  • @BekimBacaj If it does what? – Oriol Jul 15 '16 at 02:30
  • he is wrong about checking for his custom attribute by `if(!Element.prototype.toggleAttribute)` syntax. Should use the `in` operator instead e.g.: `"id" in Element.prototype`. – Bekim Bacaj Jul 15 '16 at 02:41
  • @BekimBacaj The `in` operator runs the [[HasProperty]] internal method. What if `Element.prototype` has a custom [[HasProperty]] internal method which does something unexpected as a sideways effect? It's not a standard object, after all. I guess that would be improvable; but you will have to define the function in some way, and that is more likely to fail. – Oriol Jul 15 '16 at 02:43
0

In my assessment: no

Massive overwriting Element.prototype slow down performance and can conflict with standardization, but a technical reason does not exist.

Martin Wantke
  • 4,287
  • 33
  • 21
0

I'm using several Element.prototype custom methods. so far so good until I observe a weird behaviour.

<!DOCTYPE html >
<html >
<body>
<script>
function doThis( ){
    alert('window');
}
HTMLElement.prototype.doThis = function(  ){
    alert('HTMLElement.prototype');
}
</script>
<button onclick="doThis( )" >Do this</button>
</body>
</html>

when button is clicked, the prototype method is executed instead of the global one. The browser seems to assume this.doThis() which is weird. To overcome, I have to use window.doThis() in the onclick.

It might be better if w3c can come with with diff syntax for calling native/custom methods e.g.

myElem.toggleAttribute() // call native method
myElem->toggleAttribute() // call custom method
-1

Is there any technical reason not to extend the Element prototype.

Absolutely none!

pardon me: ABSOLUTELY NONE!

In addition the .__proto__, was practically an a illegal (Mozilla) prototype extension until yesterday. - Today, it's a Standard.


p.s.: You should avoid the use of if(!Element.prototype.toggleAttribute) syntax by any means, the if("toggleAttribute" in Element.prototype) will do.

Bekim Bacaj
  • 5,707
  • 2
  • 24
  • 26
  • 6
    How can you be sure? Do you have any sources? What exactly are the assumptions, the limitations, that when broken will cause the code to fail? – Bergi Jul 15 '16 at 02:46
  • Try "extending" `Element.prototype` with a new `id` property. `if(Element.prototype.id)` throws. `Element.prototype.id = 123` throws. Similarly, adding `toggleAttribute` is not future-proof. – Oriol Jul 15 '16 at 02:50
  • 3
    @BekimBacaj: That's unlikely, I'm definitely older than JS itself. And there definitely are circumstances under which the code will not behave as expected, so I think a proper answer should include an assessment on the circumstances necessary for the code not to fail. – Bergi Jul 15 '16 at 03:00