PDA

View Full Version : ExtJS DataView and Click Events



neverness
25 Nov 2010, 10:22 PM
Hi

I may be new to ExtJS, but I'm loving it! :) However, I've hit a small snag and since I've seen some really helpful posts on this forum, I thought I'd ask to see if anyone else has encountered the same issue. Apologies if this is answered elsewhere, I have done a pretty thorough Google plus looked through the forums without much luck yet.

Anyway, I'm using a DataView to display my data in a way that I'm really happy with, in terms of both functionality and visual appeal. I'm only having one problem, which is that single-click events on my nodes seem to be forced to operate at the node level as defined by the DataView (specifically, at the level defined by the itemSelector configuration option). But what can I do when I have HTML elements inside a node that might require click events of their own? For example, normal <a> links, or HTML5 <video> elements, that kind of thing - they need to receive a click to act like you'd expect them to. But the DataView seems to eat the events.

In my example below, I'm trying to use a DataView to draw a simple list of people, where the usual selection model can apply (click to select one user, Shift-click to select multiple users etc, and query the DataView to get the selected users). But I'd also like each node have some internal interactive content, such as a direct email link to the user, that could be clicked and would act as expected (pop up an email window, in this case). Whether that also selects the node in the DataView sense isn't hugely important to me, though it'd be nice to have control over that by suppressing or passing the event through, as normal.

It's easy to use the DataView and a template to draw each node exactly the way I want it, including the interactive elements. But, when I click on any of the embedded mailto links, the DataView's click event fires, but the actual link itself doesn't get the click. In one of my other experiments I embedded an HTML5 <video> tag inside the node, and it also failed to respond to click events - so the Play/Pause and Mute buttons didn't work, though the timeline and volume sliders did work, and the right-click menu could be used to access some of the missing functionality.

Basically, I'm looking for a way to allow the sub-element within the node to receive the click event, and presumably act like any normal event at that point (so, it could suppress it or allow it to bubble upward).

If I leave out the itemSelector property on the DataView, the click events DO seem to go to the right object... at least, the "node" argument to the click listener is the actual item I clicked on, not just the parent node, and I guess I could send the event through manually at that point. But the DataView itself acts pretty strangely without itemSelector, which is clearly marked as mandatory in the docs, so I don't want to leave it out.

Any help would be appreciated!


var crewStore = new Ext.data.ArrayStore({
storeId: 'crewStore',
idIndex: 0,
fields: [
'name',
],
data: [['alan'],['bob'],['caroline'],['dylan']],
});

var tpl = '<tpl for="."><div class="thumb-wrap" id="{name}"><img src="../ext-3.3.0/examples/shared/icons/fam/user_comment.png"><a href="mailto:{name}@somewhere.com">{name}</a></div></tpl>';

// Build our DataView.
var dataPanel = new Ext.Panel({
id: 'dataPanel',
frame: false,
layout: 'fit',
items: new Ext.DataView({
store: crewStore,
tpl: tpl,
autoHeight: true,
multiSelect: true,
singleSelect: false,
frame: true,
itemId: 'dataView',
itemSelector: 'div.thumb-wrap',
overClass: 'x-view-over',
listeners: {
click: function(view,index,node,event){
if( window.console ) console.log('dataView.click(%o,%o,%o,%o)',view,index,node,event);
},
beforeclick: function(view,index,node,event){
if( window.console ) console.log('dataView.beforeclick(%o,%o,%o,%o)',view,index,node,event);
},
},
}),
});

var theLayout = new Ext.Viewport({
layout: 'border',
layoutConfig: {
animate: false,
},
items: [{
region: 'center',
margins: '5 5 5 5',
layout: 'fit',
items: dataPanel,
}],
});


Thanks!

--
neverness

Animal
25 Nov 2010, 10:32 PM
You will need to interrogate the single click event in the DataView's single click listener to see which class of element was clicked upon, and react (or not) accordingly.

neverness
27 Nov 2010, 2:39 AM
Thanks for the quick reply, Animal. I spent some time in Firebug looking at every member of the event object that's passed in as the 4th argument to the DataView's single-click event handler. Everything that referenced a DOM element, referenced the root of the DataView node, not the actual thing I clicked on (in my example above, the link is what I clicked on). I couldn't see any class identifier that'd help me determine the real node that was clicked. I looked in event.target, event.browserEvent, event.browserEvent.target and event.browserEvent.currentTarget... none of those included the <a> element I clicked.

madkris
27 Nov 2010, 3:31 AM
Hi,

I current have a similiar situation to yours.


// the data view node
<div class="item-wrap">{name}</div>

// the element appended to the node on initial click
<ul class="subitems"><ul>

// a list of subitems appended to the node together with ul element
<li class="subitem item">Item 1</li>
<li class="subitem item">Item 2</li>
I wanted to be able to support the following:
- subquent clicks on the node will not append subitems
- fire an event when a subitem is clicked

As Animal stated

single click listener to see which class of element was clicked upon, and react (or not) accordingly. I am a beginner with DOM manipulation, forgive me for asking but how do I do that in Ext JS given the HTMLelement returned by calling event.getTarget()?


I tried searching google and found this file (http://docs.google.com/viewer?a=v&q=cache:dcFe7LvI2KwJ:read.pudn.com/downloads95/doc/380603/ExtJSDomNavigation.ppt+manipulating+HTMLelement+ext+js&hl=en&gl=ph&pid=bl&srcid=ADGEESjXMNv5qAo4qg4o9GM4EkuelIHBxKArRD_Nkn6KOugCM1VqTOVjZpxbMJ6A1uKJy1aJn0SynVYJA0EyLQT1933SE0AeSZ_2gMAzILhd0hh5DnVDgGnAaWQ-QrjaHXsucmnmY2IE&sig=AHIEtbRRAAKo5pF_XZm34-ejcm4vx3Bx2g), and tried.


var el = new Ext.Element(event.getTarget());
console.log(el);
console.log(el.contains("ul"));But contains always returns false.

Regards,
madkris

neverness
27 Nov 2010, 5:01 AM
Ok I dug into the DataView.js source code. Turns out that if you have either singleSelect or multiSelect mode enabled, the DataView's onItemClick function actively prevents the default event behaviour from occurring.

A quick patch as follows gives me the ability to interact with things inside the node.


Ext.override(Ext.DataView, {
onItemClick : function(item, index, e){
if(this.fireEvent("beforeclick", this, index, item, e) === false){
return false;
}
if(this.multiSelect){
this.doMultiSelection(item, index, e);
//e.preventDefault();
}else if(this.singleSelect){
this.doSingleSelection(item, index, e);
//e.preventDefault();
}
return true;
},
});

It does interfere with the selection model behaviour of the DataView, though. With that patch, clicking on a link inside the node will activate the link as it should, but will also cause the DataView to react as if you clicked the node to select it... which means, if you had multiSelect enabled, a single click on a link will have a (perhaps unwanted) side-effect of also clearing any other selection you may have had, and leave you with only the node containing the link selected.

In my case, this is acceptable. I want the nodes to be interactive in a normal way, and I'm happy to slightly complicate the selection behaviour to achieve that. I'm building something like an image gallery but putting HTML5 video players in there instead of static images - the controls need to work normally! Selection is something my users would typically do separately to interacting with node contents, so I don't expect it to be a problem.

Thanks for the input, Animal and madkris, it helped me understand the problem and now I've got a solution!

--
neverness

madkris
27 Nov 2010, 5:34 AM
So have I, I came up with a slightly different solution though.


onClick : function(dv, idx, node, e) {
var dh = Ext.DomHelper;
var el = new Ext.Element(e.getTarget());

if (el.dom.className == 'item-wrap x-view-selected') {
console.log('overwrite subitems');
} else if (el.dom.className == 'subitem-wrap') {
console.log('subitem clicked!');
} else {
console.log(el);
}
}
Turns out that if you have either singleSelect or multiSelect mode enabled, the DataView's onItemClick function actively prevents the default event behaviour from occurring.@neverness- The default being the item inside the node?

neverness
27 Nov 2010, 4:09 PM
Yup that'd do the trick too. I guess your way will specifically handle your usage scenario perfectly. In my case, I have no certainty about what interactive elements I will be putting inside my nodes, but I'm willing to take the hit of a behavioural quirk in the selection model to have a solution that is pretty general.

Perhaps a combination of the two would be best - if the interactive element has some special class or attribute then the default event behaviour is allowed, otherwise it is suppressed as per the normal DataView.

--
neverness

Animal
27 Nov 2010, 10:53 PM
I am a beginner with DOM manipulation, forgive me for asking but how do I do that in Ext JS given the HTMLelement returned by calling event.getTarget()?
madkris

event.getTarget() takes a parameter...

madkris
30 Nov 2010, 4:57 AM
@Animal - Thanks, good to know.