I've got a WinJS ListView that has its items created using a templating function (the itemTemplate
option points to a function). The returned item has a div inside it that has the win-interactive
class so that it can accept input. Specifically, that div needs to be able to scroll to show more content that can fit on the ListView item.
The scrolling works perfectly with the win-interactive
class applied to the div. The problem I'm trying to solve is allowing a normal click (mouse down, mouse up) to still trigger the oniteminvoked
event on the ListView, while still allowing the div inside a ListView item to be scrolled.
I figured this would be easy: I'd bind a click event listener to the div with the win-interactive
class, and simple pass that click event up to another element on the ListView item, ensuring that eventually the oniteminvoked
event would be trigger.
I've bound the click listener to the win-interactive
div, and it's being triggered as expected. However, and I cannot figure out how to trigger a click/item invocation on another part of the ListView item. I've tried using properties of the event (currentTarget) to get parent or sibling elements and triggering events on them, but I simply can't figure out how to trigger an event on an element like event.currentTarget
.
tl;dr: How can I allow scrolling within a ListView item while still allowing a normal click to trigger the item invocation?
Overall, the ListView just handles clicks by passing them to whatever itemInvoked handler you register for the control. This means you should be able to just bypass that whole chain and invoke your handler directly with the appropriate item index, which is easy to obtain.
Within your item's click handler, if you have the ListView object handy, then list.currentItem will be the item with the focus (and obviously the one clicked), and its index property will be what normally gets passed to itemInvoked. With this you can then call your itemInvoked handler directly, building up the appropriate event object, or you can separate your handler's code into another method and call that one instead.
As a basic example, starting with a Grid app template project, I added win-interactive to the item-title element (which could by any other like your scrolling region) in pages/groupedItems/groupedItems.html:
<h4 class="item-title win-interactive" data-win-bind="textContent: title"></h4>
In the ready method of pages/groupedItems/groupedItems.js, I attached the ListView's object to the page control for later use:
var listElement = element.querySelector(".groupeditemslist");
var list = listElement.winControl;
this._list = list;
And then hooked up click listeners to the item-title elements as below. I'm doing it this way because the items are created through templates; if you have an item rendering function instead, then you can add the listeners in that piece of code directly.
var that = this;
list.addEventListener("loadingstatechanged", function () {
if (list.loadingState == "complete") {
var interactives = element.getElementsByClassName("item-title");
for (var i = 0; i < interactives.length; i++) {
interactives[i].addEventListener("click", that._itemClick.bind(that));
}
}
});
The implementation of _itemClick in my page control looks like this:
_itemClick: function (e) {
var item = this._list.currentItem;
//Can also get the index this way
var index = this._list.indexOfElement(e.currentTarget);
this._itemInvoked({"detail" : { itemIndex : item.index} })
},
where this._itemInvoked is the same handler that comes with the template.
Note that with win-interactive, you don't get the usual pointerDown/pointerUp behaviors for items that do the little down/up animations, so you might want to include those too if you deem it important (using WinJS.UI.Animation.pointerDown/pointerUp methods to do the effects).
Finally, it's probably possible to go up the chain from the e.currentTarget element inside _itemClick and simulate a click event on the appropriate parent, but I think that's more trouble than it's worth here having traced through the WinJS code that does all that. Much more direct to just call your own itemInvoked.