2

No library solutions, please, though if you know of one that does this, I'm happy to take a look at how they do it. Not terribly concerned with fall-backs and cross browser support.

I have a hierarchy (that will change):

<body>
    <div></div>
    <div>
        <div></div>
        <div></div>
        <div>
            <a>Click Me!</a>
        </div>
    </div>
</body>

I have an event listener on <a>Click Me!</a> and get an event object back. That much works. YEY!

I want the event.target dom hierarchy numerical index. basically, [0][1][2][0] (though it would probably return as an array, [0,1,2,0], and that's okay with me).

I know this can be done with iterating through the parent elements. I'm trying to avoid that iteration, if possible.

EDIT Redacting my last edit as an act of idiocy.

Randy Hall
  • 7,716
  • 16
  • 73
  • 151
  • 2
    Um, how are you going to calculate the location of the element including the parents' locations if you *don't* iterate through the parents? I am not sure that's possible. – Ryan Bigg Oct 30 '12 at 20:39
  • I'm not sure either, I am hoping there is some *magical* way to do this. Perhaps treating the DOM as an array, and getting the index of the `event.target`? These intricacies are beyond my understanding of javascript =) – Randy Hall Oct 30 '12 at 20:42
  • Perhaps another way to look at this question: does the element know its deep position in the dom in a way that can be accessed? – Randy Hall Oct 30 '12 at 20:47
  • I can't think of a way to deal with that problem without iterating. In jQuery you have the `index()` method to get the position, but I'm pretty sure it iterates in maybe some smart way. But I must ask this: the hierarchy is generated by whom? In which way?Or, in other terms, how much can you alter its elements by giving them classes or other attributes? – Polmonite Oct 30 '12 at 21:15
  • The elements will dynamically change on the page quite rapidly via ajax and other methods. I had thought of using some sort of attributes to pre-define the indexes, but that's not going to work. – Randy Hall Oct 30 '12 at 21:18
  • 1
    Maybe the right question is: what's the point of knowing the hierarchy index, and isn't there another way to achieve your real goal? – Christophe Oct 30 '12 at 21:34
  • Ok, then another question :P How are the items rendered (if they are rendered)? Are they, by any chance, rendered with a tree-view? Maybe we can understand the index of an element looking at its distance from top and left. (I'm trying to look at the problem from another way :D ) (PS: sorry for my bad english) – Polmonite Oct 30 '12 at 21:36
  • @Christophe I'm attempting to create an array in javascript that mimics the position of elements in the dom. I want to know the "path" to the target element so that I can store a string (or whatever) in an array with the same "path". Kyle Campbell's answer is probably the most straightforward, but I'm looking for some magic property of the dom object, or the event.target reference, that knows that path. – Randy Hall Oct 30 '12 at 21:44
  • @RandyHall: It seems wrong to me to build a map of the DOM, which is quite performant tree already. "in an array with the same path" sounds too much like `element.children` (or a static copy of it). What do you actually want to do, how/why would you use those path strings for that? – Bergi Oct 31 '12 at 00:22
  • @Bergi I have a crazy notion about storing "data" and "functions" in an array that mimics the dom structure. This way, I could keep data compartmentalized without actually going back to the dom to get attributes: I could just look at a neat, fast, numerically-indexed array using the "path" of an event.target. Of course, I'm fairly certain any performance gain from doing that would instantly be destroyed if I am iterating over the target parents, which is why I'm looking for some native method or property. – Randy Hall Oct 31 '12 at 03:20

3 Answers3

6

A way to get an index without explicit iteration is to use the array indexOf method.

Here is what it would look like, e being your event:

function getIndex(e){
  var t=e.target;
  return Array.prototype.indexOf.call(t.parentNode.childNodes,t);
}

There's a number of gotchas with this technique. First, indexOf is only supported by the most recent browsers. Second, you need the call trick because a NodeList is not an array. Third, childNodes might return more nodes than expected and then you'll have to filter by node type.

To get the whole hierarchy index, just rince and repeat as you climb up the DOM hierarchy with parentNode.

For the record, e.target is not cross-browser either, but I see this is what you're already using.

[Update] The full code, using children instead of childNodes:

function getIndex(e) {
var index=[],
    t=e.target;

while (t!==document.body) {
    index.unshift(Array.prototype.indexOf.call(t.parentElement.children,t));
    t=t.parentElement;
}
return index;
}
Christophe
  • 27,383
  • 28
  • 97
  • 140
  • Could use `t.parentElement.children` as the first argument, should only return elements, not nodes. At least, that's how the event target appears to be laid out in Chrome. – Randy Hall Oct 31 '12 at 13:40
  • @RandyHall right, here again you would have to consider browser support for "children". – Christophe Oct 31 '12 at 15:26
  • true... I'd have to look into it. My main concern is 'modern' browsers (mobile, mostly). – Randy Hall Oct 31 '12 at 15:29
  • I have updated the reply with a second, more complete snippet using children. – Christophe Oct 31 '12 at 15:58
  • Good update. I'm accepting this as "most viable", though I WISH there were some magical property, there doesn't appear to be. – Randy Hall Oct 31 '12 at 16:15
  • Array `indexOf` is only supported by the most recent browsers? Really? The only browsers it really isn't supported on is IE < 9. – Ian Nov 08 '12 at 19:44
  • @Ian just a couple weeks ago IE9 was still Microsoft's most recent browser. And it is still the most recent on Windows 7. – Christophe Nov 08 '12 at 19:53
  • Who said anything about specifically Microsoft's most recent browser? Of any of the browsers used, the only ones that don't support it are < IE 9. There's tons of other browsers than just IE 9 that represent "only the most recent browsers". – Ian Nov 08 '12 at 20:05
3

I know you want to avoid iteration, but it's probably the most straight forward strategy:

$(document).on('click','a', function(e){
  var result=[];
  var count = function(e){
    if(e.parentNode != document.body ){
      result.push(e);
      count(e.parentNode);
   }else {
      return result;                                    
   }
 };
 count(e.target)
 console.log(result);
});

For what reasons are you trying to avoid iteration? Is the dom huge or something?

Kyle Campbell
  • 186
  • 1
  • 4
  • Potentially huge dom, with events being called often, in a mobile environment >.< Just looking into the possible viability of my crazy ideas. – Randy Hall Oct 30 '12 at 21:08
  • Please, either use iteration *or* recursion, but don't mix them. Iteration (without function calls) would be faster, btw – Bergi Oct 31 '12 at 00:14
  • Your end condition is flawed. There may be nodes that receive a click event but do not have `document.body` in their ancestors – Bergi Oct 31 '12 at 00:16
1

You say that "The elements will dynamically change on the page quite rapidly" and that you are "attempting to create an array in javascript that mimics the position of elements in the dom". It means that you'll need to recalculate not only the index path of the element(s) that moved, but also the index path of the ones that didn't move.

Bottom line: instead of calculating the path of an individual element, it makes sense to me to iterate through the whole hierarchy, as anyway you'll need to recalculate not one, but all index paths.

Christophe
  • 27,383
  • 28
  • 97
  • 140
  • As I'm adding new elements to the dom, I can slice them into the congruent array if I know the index path. – Randy Hall Oct 30 '12 at 22:04
  • @RandyHall I see. You don't really care about the index path, it's just an intermediary calculation step. – Christophe Oct 30 '12 at 22:17
  • Bonus question: how do you plan to proceed when a node is REMOVED from the DOM? – Christophe Oct 30 '12 at 22:24
  • @Cristophe splice the index as it's removed from the dom. I have custom functions that can handle the dom add/remove "listeners" since the old standard methods were, well, slow, unstandard, and now deprecated. – Randy Hall Oct 31 '12 at 03:13
  • @RandyHall I'd be interested to see how you do it! I never got a good answer to this http://stackoverflow.com/questions/4424923/library-for-dom-mutation-events – Christophe Oct 31 '12 at 04:29