Monday, October 19, 2015

XPATH vs. jQuery selectors

I've always wondered if there was a good way to connect a button with an input field within a repeated list item that didn't involve generating unique IDs with index numbers. Wouldn't it be nice if you could just say, 'this button is connected to that input field a few DOM nodes away'?

Well, it turns out you can!

In the example below the 'data-control-xpath' attribute references the drop down list that accompanies the button. The selector uses XPATH rather than a jQuery selector -- the good news is that XPATH can be natively interpreted in Javascript. The XPATH query below translates to: "find some select element that exists in the grandparent of this node', where 'this node' is the button itself (passed as a parameter to the function).

HTML
<a href="#" data-control-xpath="../..//select" data-id="@Model.Id" onclick="return associateCategoryWith(this);" class="btn btn-primary">Assign</a>

Javascript
function associateCategoryWith(button) {
var $button = $(button),
$list = $xpath($button.data("control-xpath"), button),
categoryId = $list.val();
[...]
}
function $xpath(xpath, contextNode) {
return $(document.evaluate(xpath, contextNode, null, XPathResult.ANY_TYPE, null).iterateNext());
}

Other ways around this problem include building a KnockoutJS or AngularJS template and classes or, as mentioned, generating unique IDs with an index variable.

I guess you could also do it with jQuery '.parents()' and a 'filter()', but that would be no fun. ;-)