8

Is there a way I could emulate jQuery's .parents() method in Angular, without having to actually include jQuery?

The final goal is to get all the parents of a DOM element.

EDIT: Why I need this?

I'm creating a directive (a dropdown-like widget). The dropdown should listen the entire <body> for clicks and close itself (if it's open) if a click is made outside of the widget's area.

Now, I know how to create a simple directive that would listen for mouse events, like this one:

app.directive('mouseTrap', function() {
  return function(scope, elem) {
    elem.bind('click', function(event) {
      scope.$broadcast('click', { event: event } );
    });
  };
});

...which I would then use like this: <body mouse-trap .... > and

$scope.$on('click', function(msg, obj) {
  console.log("click!");
});

That is where I need to check if any of the parents of the clicked object is the top-level div of my widget, and if not, close the widget.

alexandernst
  • 14,352
  • 22
  • 97
  • 197
  • 1
    so why you think you need this? usually in angular not works woth DOM directly, anyway angular use inside jqLite – Grundy May 25 '15 at 16:43
  • 1
    @Grundy But does jqLite support `.parents()` method? I don't see it in DOC – A. Wolff May 25 '15 at 16:47
  • @alexandernst, methinks you need start think not in jQuerym but in angular, try see [this](http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background/15012542) – Grundy May 25 '15 at 16:53

4 Answers4

7

Angular's jqLite supports parent() method, so you could get all the parents in a loop like this:

var p = element.parent();
var allParents = [];
while (p.length > 0) {
    allParents.push(p[0]);
    p = p.parent();
}
4

I'm not certain you'd want to be using DOM parent references when trying to interface with Angular controllers. But not knowing what your goal is, the standard methods .parentNode, is what you're looking for. Then you'd have to loop until you hit body, or html, or null. So if you want native javascript:

var currentParent = nodeToFindParentsOf.parentNode();
var parents = [];
while(currentParent){
  parents.push(currentParent);
  currentParent = currentParent.parentNode();
}

I'm also paranoid of while loops. So would tend to wrap that whole thing in a function and put a safety valve on it to let me know what's going on, in case of crazyness in my code or in the DOM, so I'd get a clean error, instead of locking up the browser.

function getAllParentsOfNode (nodeToFindParentsOf) {
    var currentParent = nodeToFindParentsOf.parentNode();
    var parents = [];
    var safetyCount = 1000;
    while(currentParent){
      parents.push(currentParent);
      currentParent = currentParent.parentNode();
      if (--safetyCount  === 0) {
        throw new Error("Something went wrong. Found 1000+ parents!")
      }
    }

    return parents;
}
Cooper Buckingham
  • 2,503
  • 2
  • 15
  • 23
  • 1
    Matter of taste, but could shorten a bit: var aParent = nodeToFindParentsOf.parentNode(); var parents = []; while(aParent){ parents.push(aParent); aParent = aParent.parentNode(); } – tsemer Aug 09 '17 at 12:50
3

Angularjs includes jqLite which contains some of the jQuery functions. details here: https://docs.angularjs.org/api/ng/function/angular.element

As noted in the site, it includes

parent() - Does not support selectors

The function provided is similar to the jQuery parent() function but will not take selectors, so you wont be able to filter using selector. From the jQuery documentation:

This method is similar to .parents(), except .parent() only travels a single level up the DOM tree

So short answer: no it doesnt. But it does provide a small subset of it.

suvartheec
  • 3,484
  • 1
  • 17
  • 21
-2

EDIT: Although this answer was accepted, it's not correct. See comment and better answers below.

It does look like jQLite supports this already so if you're using angular you should be able to do this without pulling in JQuery itself.

From the docs here:

Supported instance methods: (links to jQuery Docs) A subset of the operations which jQuery supports are currently implemented. All of them are implemented using the chained operation syntax of jQuery. e.g:

$("div.foo").each(function() {
  $(this).removeClass("foo").addClass("bar");

});

$("div.bar, div.baz").css({ "border": "1px solid red"    }).children().addClass("child");



.parent([selector)]
.parents([selector)] 
Ruairi O'Brien
  • 1,209
  • 5
  • 18
  • 33
  • 4
    JQLite in this case(question) is the version that comes with angular and it does not support the .parents() method. However is has a .parent() method which does not accepts parameters / selectors. So your answer here is missleading – webicy Mar 02 '16 at 12:12
  • @webicy: you're right! I'll edit my answer and try to have it deleted. Not sure how you're supposed to manage something like this. Thanks for the comment. – Ruairi O'Brien Jun 08 '16 at 19:13