# Extending DOM elements with prototype for easier function execution

I've creating a small library of JavaScript functions that I will be using. Some are for UI functions others are to return something.

I am wondering if this method is ok and not a bad move before going any further with this library. Also, please do not mention jQuery as this is all JavaScript!

HTMLElement.prototype.drag = function(bool) {
var self = this;
event.stopPropagation();
event.preventDefault();
var origLeft= parseInt(self.offsetLeft,10);
var origTop = parseInt(self.offsetTop,10);
var mdX = event.clientX;
var mdY = event.clientY;
var elements = [];
if(bool === true){
var j = window.localStorage.getItem('userSettings');
if(j){
self.style.left= j.left;
self.style.top = j.top;
}
}
function drag(bool) {
self.style.position="absolute";
self.style.zIndex=999;
self.style.left = origLeft + event.clientX - mdX + "px";
self.style.top = origTop + event.clientY - mdY + "px";
event.stopPropagation();
}
function stop(bool){
self.classList.remove('draggable');
self.style.zIndex=0;
document.removeEventListener('mousemove',drag,true);
document.removeEventListener('mouseup',stop,true);
if(bool === true){
var settings = {
left: self.style.left,
top: self.style.top
};
var b = JSON.stringify(settings);
window.localStorage.setItem('userSettings',b);
console.log(b);
}
event.stopPropagation();
}
};


The above code is of course for a draggable function. Here is a getClientRects() sort of function but better:

HTMLElement.prototype.getRect = function(){
var a = this.offsetLeft;
var b = this.offsetTop;
var c = this.offsetHeight + b;
var d = this.offsetWidth + a;
var f = this.offsetHeight;
var g = this.offsetWidth;
var obj = { };
obj.left = a;
obj.right=d;
obj.top=b;
obj.bottom=c;
obj.width = g;
obj.height=f;
return obj;
};


The purpose of showing you all this is to get your thoughts and maybe advise about prototype or should I not do it this way. I just like the method execution like this:

document.getElementById('div').getRect();


instead of doing it with a function getRect('div');.

The problem is for one not sure what enumerable is as I've self taught everything, and never really read on the terminology. I know what one of the users means when he says iterating them using a for in loop though not sure how to make it non-enumerable. Nor how to go about this any better way as I don't want my users (who use jQuery a lot) though the library is huge and honestly I'd rather them use a vanilla JavaScript library in the sense that is minimized for purposes of what I am doing (making a huge plug-in with a library and other coding options they can have their hands on).

• There is no need to make is enumerable...just use defineProperty to add functionality. – vsync Aug 10 '15 at 13:02

I don't have any insight on extending host objects, because I've never done it, and quite frankly this scares me a little bit.

That said, I can shed a bit of light on that "enumerable" problem.

Objects in javascript are traversable, much like arrays. This means that when you do :

var o = {foo: "bar", baz: "buzz"};
for(var property in o){
console.log(o[property]);
}


you get "bar" and "buzz" in the console.

Now in a for-in loop, the only properties that you get are those who are enumerable. What does this mean ? Basicly, this means that you only get properties for which o.propertyIsEnumerable('propertyName') is true (and by extension, for which o.hasOwnProperty('propertyName') is true).

When you create a property on an object, by default it is enumerable, and hasOwnProperty returns true for it. But it is possible to define properties with modifiers :

   o = {};
Object.defineProperty(o, 'foo', {
value:      function(){ console.log('foo'); },
enumerable: false
});

o.foo();                      // "foo"
o.propertyIsEnumerable('foo') // false
o.hasOwnProperty('foo')       // false
for(property in o){
console.log(property); // this will never be reached - o has no enumerable property yet
}


One reason to mark certain properties as not enumerable is that when extending an object, one usually copies all enumerable properties from a source object using a for-in loop. When writing a lib, you have to care about this, because if you don't your users will have to deal with properties they probably are not aware of or don't care about.

Also note that every fresh object already has non enumerable properties : prototype, hasOwnProperty, etc...

• Ok so I have to use Object.defineProperty(o being the object, foo being the property of o and value is the properties value and then enumerable false makes it non-enumerable. Do I have to do this to each property, hence defineProperty and not defineProperties – EasyBB Nov 6 '13 at 16:14
• no, see... Object.defineProperties – m_x Nov 6 '13 at 18:20
• Ok thank you, I'll read on it. If all is well then I'll mark this as the accepted answer. – EasyBB Nov 6 '13 at 23:34
• I understand now. Though I am now getting in FF something about property only has a getter or something like that – EasyBB Nov 7 '13 at 4:14

My suggestions on whether to extend native objects or not:

I would not do it. Here are some reasons based on your specific code:

• Lets say browsers decide to implement their own "drag" method in the future. If this happens and your drag function doesn't match theirs, then you'll have the confusing case where your code differs from the standard Javascript interface any other developers know and use. Your code would also be hiding the functionality of the browser's new "drag" method, which may be completely different.

• A problem specific to how you are extending DOM elements: different browsers have different interfaces to the DOM and certain DOM prototypes may not be available in certain browsers. This is described in this article: http://perfectionkills.com/whats-wrong-with-extending-the-dom/. For instance, Internet Explorer 7 does not expose DOM elements like Node or Element. Although you are not touching Node or Element, it would be easy to make this mistake if you being extending DOM elements.

• your first argument is not an issue since you can check if such functionality is previously declared, then do not proceed with your custom one. Your second argument had more weight in the past, but nowadays things are much more stable. – vsync Aug 10 '15 at 13:05