Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit c03db65

Browse files
crisbetoThomasBurleson
authored andcommitted
feat(util): add the ability to pass in a predicate function
* It could be useful to be able to match node in `getClosest` by more than their tag name. This change adds the ability to pass in a function instead. * Adds a few unit tests for `getClosest`, since it didn't have any. Closes #8644
1 parent 0315713 commit c03db65

File tree

2 files changed

+72
-4
lines changed

2 files changed

+72
-4
lines changed

src/core/util/util.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,19 +472,29 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
472472
* getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching nodeName
473473
*
474474
* @param el Element to start walking the DOM from
475-
* @param tagName Tag name to find closest to el, such as 'form'
475+
* @param check Either a string or a function. If a string is passed, it will be evaluated against
476+
* each of the parent nodes' tag name. If a function is passed, the loop will call it with each of
477+
* the parents and will use the return value to determine whether the node is a match.
476478
* @param onlyParent Only start checking from the parent element, not `el`.
477479
*/
478-
getClosest: function getClosest(el, tagName, onlyParent) {
480+
getClosest: function getClosest(el, validateWith, onlyParent) {
481+
if ( angular.isString(validateWith) ) {
482+
var tagName = validateWith.toUpperCase();
483+
validateWith = function(el) {
484+
return el.nodeName === tagName;
485+
};
486+
}
487+
479488
if (el instanceof angular.element) el = el[0];
480-
tagName = tagName.toUpperCase();
481489
if (onlyParent) el = el.parentNode;
482490
if (!el) return null;
491+
483492
do {
484-
if (el.nodeName === tagName) {
493+
if (validateWith(el)) {
485494
return el;
486495
}
487496
} while (el = el.parentNode);
497+
488498
return null;
489499
},
490500

src/core/util/util.spec.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,4 +522,62 @@ describe('util', function() {
522522
}));
523523
});
524524
});
525+
526+
describe('getClosest', function() {
527+
var $mdUtil;
528+
529+
beforeEach(inject(function(_$mdUtil_) {
530+
$mdUtil = _$mdUtil_;
531+
}));
532+
533+
it('should be able to get the closest parent of a particular node type', function() {
534+
var grandparent = angular.element('<h1>');
535+
var parent = angular.element('<h2>');
536+
var element = angular.element('<h3>');
537+
538+
parent.append(element);
539+
grandparent.append(parent);
540+
541+
var result = $mdUtil.getClosest(element, 'h1');
542+
543+
expect(result).toBeTruthy();
544+
expect(result.nodeName.toLowerCase()).toBe('h1');
545+
546+
grandparent.remove();
547+
});
548+
549+
it('should be able to start from the parent of the specified node', function() {
550+
var grandparent = angular.element('<div>');
551+
var parent = angular.element('<span>');
552+
var element = angular.element('<div>');
553+
554+
parent.append(element);
555+
grandparent.append(parent);
556+
557+
var result = $mdUtil.getClosest(element, 'div', true);
558+
559+
expect(result).toBeTruthy();
560+
expect(result).not.toBe(element[0]);
561+
562+
grandparent.remove();
563+
});
564+
565+
it('should be able to take in a predicate function', function() {
566+
var grandparent = angular.element('<div random-attr>');
567+
var parent = angular.element('<div>');
568+
var element = angular.element('<span>');
569+
570+
parent.append(element);
571+
grandparent.append(parent);
572+
573+
var result = $mdUtil.getClosest(element, function(el) {
574+
return el.hasAttribute('random-attr');
575+
});
576+
577+
expect(result).toBeTruthy();
578+
expect(result).toBe(grandparent[0]);
579+
580+
grandparent.remove();
581+
});
582+
});
525583
});

0 commit comments

Comments
 (0)