1

What I have:

A list of groups that basically consists of main data points: link, name and cover image. Each group's code looks like this:

<a href="/group/ololo" class="group-item">
    <h1>Group Name</h1>
    <img src="path/to/the/image.png" />
</a>

Therefore a.href is the link, h1 consists the name and img.src is the image.

What I need:

I need to create a model representation of each group, so that I can access it simply with group.link, group.name and group.image.

I consider creating a class with constructor and setting all the fields manually and it, actually, works. What I want to do is extend the Node class for it to add those fields to the object, if I'm trying to access a.group-item. An example of what I want are special Nodes, like input[type=text], that has a .value variable or input[type=checkbox] that has .checked variable.

How is it possible to extend the Node class that way? Also, I do not want to use any frameworks for that, only pure js. Thank you!

JaffParker
  • 668
  • 8
  • 21

1 Answers1

1

Is there a specific reason for why you want to extend the DOM? If not, you can just define a function that returns the nested attributes that you want:

var group = document.getElementsByClassName('group-item');

var items = Array.prototype.map.call(group, item);

function item(element) {
    return {
        link: element.href,
        name: element.getElementsByTagName('h1')[0].innerText,
        image: element.getElementsByTagName('img')[0].src
    };
}

http://jsbin.com/xidufiyara/1/edit?html,js,console

Otherwise, here is a post that answers your question about extending the DOM: In Javascript, can you extend the DOM?

Edit: Here's a solution using Object.defineProperty:

function configureGroupItem(groupItem) {
    Object.defineProperty(groupItem, 'link', {
      enumerable: true,
      get: function() {
        return groupItem.href;
      }
    });

    Object.defineProperty(groupItem, 'name', {
      enumerable: true,
      get: function() {
        return groupItem.getElementsByTagName('h1')[0].innerText;
      },
      set: function(val) {
        groupItem.getElementsByTagName('h1')[0].innerText = val
      }
    });

    Object.defineProperty(groupItem, 'image', {
      enumerable: true,
      get: function() {
        return groupItem.getElementsByTagName('img')[0].src;
      },
      set: function(val) {
        groupItem.getElementsByTagName('img')[0].src = val
      }
    });
}

Here's how I applied and used the properties:

var group = document.getElementsByClassName('group-item');

Array.prototype.map.call(group, configureGroupItem);

for (var i = 0; i < group.length; ++i) {
  var item = group[i]

  // set item name
  item.name = 'Group' + i

  // set item image
  item.image = 'http://placehold.it/' + (i+4) + '0x' + (i+2) + '0'

  // print the data
  console.log(item.name + ' ' + item.image + ' ' + item.link)
}

Of course, this won't be compatible with older browsers. Another reason why this should be discouraged is because other libraries / javascript plugins could define / manipulate the same properties, so you introduce the possibility of conflicts with other code.

Community
  • 1
  • 1
Desmond Lee
  • 707
  • 6
  • 17
  • There is no specific reason at all, it's just a question of knowledge and curiosity. Because I already have a working class that can do everything. The only thing I'd like to achieve is this: `document.querySelector('a.group-item').name; // 'Group Name'` – JaffParker Mar 10 '15 at 21:37
  • I found a way to do what you described using [`Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty): http://jsbin.com/hecusibegi/2/edit?html,js,console,output I modified my answer as well – Desmond Lee Mar 11 '15 at 17:38