0

If I use get with defineProperty

Object.defineProperty(Object.prototype,'parent',{
    get:function(){return this.parentNode}
});

and I can call it like: document.body.parent, then it works.

When I use value with defineProperty

Object.defineProperty(Object.prototype,'parent',{
    value:function(x){
        var temp=this.parentNode;
        for(var i=1;i<x;i++){temp=temp.parentNode};
        return temp
    }
});

I can call it like: document.getElementsByName("newtag").parent(2), means to find the parent node of newtag's parent node.

But when I put them together it says Uncaught TypeError: Invalid property. A property cannot both have accessors and be writable or have a value.

How can I do it so that I can call it both ways, .parent & .parent(n)?

No jQuery

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
Naz
  • 2,520
  • 2
  • 16
  • 23
  • 1
    I know it's off topic, but why are you extending `Object.prototype` in order to enhance DOM elements? It doesn't seem useful to add DOM-centric methods to *all* objects. –  Jun 16 '13 at 15:44
  • @CrazyTrain I understand your concern... but whatever I am doing is needed... – Naz Jun 16 '13 at 15:53
  • @BlackCobra: No, you cannot create a value that is both a DOM node and a function. And that's not even something to do with mixing descriptors of accessor and data properties. Please tell us [what your actual problem is](http://meta.stackexchange.com/q/66377) - what you would need this weird DOM extension for - and we might be able to suggest a better solution. – Bergi Nov 08 '15 at 21:47

2 Answers2

0

No, you can't unfortunately. Instead you can create a bit different function that returns the first parentNode by default(if no parameter specified), otherwise it counts n parents.

Object.prototype.parent = function (n) {
    if (!this.hasOwnProperty('parentNode')) return null;
    if (!n || n === 1) return this.parentNode;
    var temp = this.parentNode;
    for (var i = 1; i < n; i++)
        temp = temp.parentNode;
    return temp;
};

Some notes: first of all, you should check if the Object has a property parentNode, otherwise the script will raise an exception. I used hasOwnProperty, but you can remove that line if you extend just HTMLElement.

Also, if n is 0 or 1, or it is not defined it will return the element's parentNode. Use it as element.parent() or element.parent(2).

Niccolò Campolungo
  • 11,824
  • 4
  • 32
  • 39
  • the first one is a key... not a function... `.parent`... the second one is a function... `.parent(n)`... but you are creating a function for both... I need a key and a function with same name and as prototype of `Object`... – Naz Jun 16 '13 at 15:50
  • 1
    "No, you can't unfortunately. [...]" – Niccolò Campolungo Jun 16 '13 at 15:51
  • I think there is a way to do that... maybe not by using `defineProperty`... but there is something... – Naz Jun 16 '13 at 15:54
  • @BlackCobra: Think about how you'd define this. Whatever you do will be based upon some code doing `elem.parent`. At that point, you can't predict what will come next. Should `elem.parent` return a function that can be invoked, or should it return a DOM element? The only way to accomplish it would be if you could make arbitrary objects "callable". Then you could return the parent, but make it callable so that it'll behave like a function. But JavaScript doesn't give that option. Closest you'd get would be to return a function, but before you do, copy the parent's properties onto it. Yuck! –  Jun 16 '13 at 16:00
0

MDN describes the reason for the error

Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.

There is reason for this: the way you want would be ambiguous: if parent() was a function, then parent would return that function and the getter?

Also do not change object that you don't own. Such code would not be maintainable: if someone would define its own Object.prototype.parent() in his library, you could not use it. Prior to any use of any code, you would need to track down what was changed. This would be the same effort as writing everything from scratch.

Object.prototype is particularly bad idea to change: by the prototype you add the parent() function to every array, JSON object, webAPI objects... they don't have parentNode, so that function is completely useless to them, it is just a performance burden.

The previous two paragraphs are the reason why we have Object.defineProperty and not Object.prototype.defineProperty. Note that if it would be so, you could code myAPI.defineproperty(...) in the code below, which is shorter, BUT... the performance and the design... schrecklich.

You could code something like this

var myAPI = {
  node: document.body,
  level: 1
};

Object.defineProperty(MyAPI,'parent',{
    get:function(){
        var temp=MyAPI.node.parentNode;
        // some sanity check should be performed here
        for(var i=1;i<MyAPI.level;i++){temp=temp.parentNode};
        return temp;
    }
});

myAPI.node = document.getElementById("myNode");
myAPI.level = 2;
var grandFather = myAPI.parent; // actually returns the grandparent

but I doubt it would be useful.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169