I am unable to capture "click" event in my gltf model in a-frame. Here is the code. A-frame documentation shows only two events are supported. Namely "model-loaded" and "model-error". How do we go about supporting other events such as "Click" and "mouseenter"?
AFRAME.registerComponent('material-displacement', {
/**
*/
init: function () {
this.material = new THREE.MeshStandardMaterial({color: "green"});
this.el.addEventListener('model-loaded', () => { this.update(); });
},
/**
* Apply the material to the current entity.
*/
update: function () {
const mesh = this.el.getObject3D('mesh');
if (mesh) {
mesh.traverse((node) => {
if (node.isMesh) node.material = this.material;
});
}
},
});
You implement them just as you would think you do. Instead of Having the Event Listener listen to a model-loaded event you make it listen to a click or mouseenter event.
So change this line
this.el.addEventListener('model-loaded', () => { this.update(); });
to this line
this.el.addEventListener('click', () => { this.update(); });
Related
I'm using the accordion component of uikit and I'm trying to extend the plugin ... this is what I wrote
UIkit.on('beforeready.uk.dom', function () {
$.extend(UIkit.components.accordion.prototype, {
init: function () {
}
});
and this is the init code that is changing:
init: function() {
var $this = this;
this.element.on('click.uikit.accordion', this.options.toggle, function(e) {
e.preventDefault();
$this.toggleItem(UI.$(this).data('wrapper'), $this.options.animate, $this.options.collapse);
});
this.update();
if (this.options.showfirst) {
this.toggleItem(this.toggle.eq(0).data('wrapper'), false, false);
}
},
as you can see with this code I can override the init function ...however I want to keep the existing init function and add some code in it....is it possible to do so?
Thanks in advance
This is a simple todo app that currently only lists a few li views within a ul collection view. I am trying to cause the click event to fire a simple showAlert function
If I keep my mouse over one of the task views(li) and refresh the page so that my mouse ends up hovering over that specific li once reloaded, the click event will fire off my showAlert function.
The problem is once I move my mouse(most likely triggering a mouseover event), I lose the click event on all task views(li) including the view I was initially hovering over.
Based on all posts I could find closely related to this problem, I've tried using this.delegateEvents() in various places, with no luck.
Preceding code
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
App.Models.Task = Backbone.Model.extend({});
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task
});
App.Views.Tasks = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.Task({ model: task });
this.$el.append(taskView.render().el);
}
});
This is the view in question
App.Views.Task = Backbone.View.extend({
tagName: 'li',
events: {
'click': 'showAlert'
},
showAlert: function() {
alert('yes!');
},
render: function() {
this.$el.html( this.model.get('title') );
return this;
}
});
proceeding code
var tasksCollection = new App.Collections.Tasks([
{
title: 'Task 1',
priority: 3
},
{
title: 'Task 2',
priority: 4
},
{
title: 'Task 3',
priority: 5
}
]);
var tasksView = new App.Views.Tasks({ collection: tasksCollection });
$('.tasks').html(tasksView.render().el);
})();
I am developing an app for iOS using Phonegap bundled with jQuery Mobile 1.1.1. I have a div on my page that is listening for both tap and taphold events.
The problem I am facing is that the tap event is fired after the taphold event once I lift my finger. How do I prevent this?
A solution is provided here but is this the only way to do this? Kinda nullifies the whole point of having two different events for tap & taphold if you need to use a boolean flag to differentiate the two.
Following is my code:
$('#pageOne').live('pageshow', function(event) {
$('#divOne').bind('taphold', function (event) {
console.log("TAP HOLD!!");
});
$('#divOne').bind('tap', function () {
console.log("TAPPED!!");
});
});
Would greatly appreciate the help. Thanks!
Simply set this at the top of your document or anywhere before you define your even:
$.event.special.tap.emitTapOnTaphold = false;
Then you can use it like this:
$('#button').on('tap',function(){
console.log('tap!');
}).on('taphold',function(){
console.log('taphold!');
});
[Tried and Tested]
I checked jQuery Mobile's implementation. They are firing the 'tap' event after 'taphold' every time on 'vmouseup'.
Workaround would be not to fire the 'tap' event if the 'taphold' has been fired. Create a custom event or modify the source as per you need as follows:
$.event.special.tap = {
tapholdThreshold: 750,
setup: function() {
var thisObject = this,
$this = $( thisObject );
$this.bind( "vmousedown", function( event ) {
if ( event.which && event.which !== 1 ) {
return false;
}
var origTarget = event.target,
origEvent = event.originalEvent,
/****************Modified Here**************************/
tapfired = false,
timer;
function clearTapTimer() {
clearTimeout( timer );
}
function clearTapHandlers() {
clearTapTimer();
$this.unbind( "vclick", clickHandler )
.unbind( "vmouseup", clearTapTimer );
$( document ).unbind( "vmousecancel", clearTapHandlers );
}
function clickHandler( event ) {
clearTapHandlers();
// ONLY trigger a 'tap' event if the start target is
// the same as the stop target.
/****************Modified Here**************************/
//if ( origTarget === event.target) {
if ( origTarget === event.target && !tapfired) {
triggerCustomEvent( thisObject, "tap", event );
}
}
$this.bind( "vmouseup", clearTapTimer )
.bind( "vclick", clickHandler );
$( document ).bind( "vmousecancel", clearTapHandlers );
timer = setTimeout( function() {
tapfired = true;/****************Modified Here**************************/
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
}, $.event.special.tap.tapholdThreshold );
});
}
};
You can use stopImmediatePropagation() method of jquery to solve this issue. According to the explanation in jquery api, stopImmediatePropagation() method
"Keeps the rest of the handlers from being executed and prevents the
event from bubbling up the DOM tree."
put this in your taphold event handler... this suggestion assumes o is a jQuery object that fired the taphold
jQuery(o).one('tap click', function(){ return false; });
the binding to the one method will fire the event only once. returning false will stop the execution of that event if it was an < a > tag.
Since swipe, triggers taphold then I was able to keep it simple with:
$(c).bind("taphold",function(e){
if(e.target.wait){
e.target.wait = false;
}else{
alert("fire the taphold");
}//eo if not waiting
});
$(c).bind("swipe",function(e){
e.target.wait = true;//taphold will come next if I don't wave it off
alert(e.target.text+"("+e.target.attributes.dataId.value+") got swiped");
return false;
});
To support tap too then I'd defer the wait clear until the tap event which will also always fire.
I still have problems, with jquery-mobile's taphold, I solved the problem of the click called after taphold, putting a timeout on the element.
JQM 1.4 with emitTapOnTaphold = false;
Example:
$(".element").on("taphold", function () {
// function her
setTimeout (function () {
$(this).blur();
400);
});
$.event.special.tap = {
tapholdThreshold: 750,
setup: function() {
var thisObject = this,
$this = $( thisObject );
$this.bind( "vmousedown", function( event ) {
if ( event.which && event.which !== 1 ) {
return false;
}
var origTarget = event.target,
origEvent = event.originalEvent,
/****************Modified Here**************************/
tapfired = false,
timer;
function clearTapTimer() {
clearTimeout( timer );
}
function clearTapHandlers() {
clearTapTimer();
$this.unbind( "vclick", clickHandler )
.unbind( "vmouseup", clearTapTimer );
$( document ).unbind( "vmousecancel", clearTapHandlers );
}
function clickHandler( event ) {
clearTapHandlers();
// ONLY trigger a 'tap' event if the start target is
// the same as the stop target.
/****************Modified Here**************************/
//if ( origTarget === event.target) {
if ( origTarget === event.target && !tapfired) {
triggerCustomEvent( thisObject, "tap", event );
}
}
$this.bind( "vmouseup", clearTapTimer )
.bind( "vclick", clickHandler );
$( document ).bind( "vmousecancel", clearTapHandlers );
timer = setTimeout( function() {
tapfired = true;/****************Modified Here**************************/
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
}, $.event.special.tap.tapholdThreshold );
});
}
};
#Akash Budhia: Thanks for your solutions.
It's great, sounds it work for me!
I have a Backbone.js project which uses a comparator function defined in the collection. It sorts items when the page is refreshed, but I am trying to get it to sort when a button is clicked instead of on page refresh. Here is my code:
var Thing = Backbone.Model.extend({
defaults: {
title: 'blank',
rank: ''
}
});
var ThingView = Backbone.View.extend({
className: 'thingClass',
template: _.template('<b><button id="remove">X</button> <b><button id="edit">Edit</button> <%= title %> Rank:<%= rank %></b>'),
editTemplate: _.template('<input class="name" value="<%= name %>" /><button id="save">Save</button>'),
events: {
"click #remove": "deleteItem",
"click #edit": "editItem",
"click #save": "saveItem",
},
deleteItem: function () {
console.log('deleted');
this.model.destroy();
this.remove();
},
editItem: function () {
console.log('editing');
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveItem: function () {
console.log('saved');
editTitle = $('input.name').val();
console.log(editTitle);
this.model.save({
title: editTitle
});
this.$el.html(this.template(this.model.toJSON()));
},
render: function () {
var attributes = this.model.toJSON();
//console.log (attributes);
this.$el.append(this.template(attributes));
return this;
}
});
var ThingsList = Backbone.Collection.extend({
model: Thing,
localStorage: new Store("store-name"),
comparator: function(thing) {
return thing.get('rank');
},
});
var thingsList = new ThingsList;
var ThingsListView = Backbone.View.extend({
el: $('body'),
events: {
'click #add': 'insertItem',
'click #sort': 'sortItems',
},
initialize: function () {
thingsList.fetch();
thingsList.toJSON();
this.render();
this.collection.on("add", this.renderThing, this);
this.collection.on("reset", this.clearRender, this);
},
insertItem: function (e) {
newTitle = $('#new-item').val();
newRank = $('#rank').val();
newThing = new Thing({
title: newTitle,
rank: newRank
});
this.collection.add(newThing);
newThing.save();
console.log(this.collection.length);
},
sortItems: function (e) {
console.log('clicked sort button');
this.collection.sort();
this.$el.detach('.item');
},
render: function () {
_.each(this.collection.models, function (items) {
this.renderThing(items);
}, this);
},
renderThing: function (items) {
var thingView = new ThingView({
model: items
});
this.$el.append(thingView.render().el);
},
clearRender: function () {
console.log('clear render called')
_.each(this.collection.models, function (items) {
//this.remove();
this.$el.remove(".thingClass")
this.renderThing(items);
}, this);
},
test: function (items) {
console.log('test worked');
},
});
var thingsListView = new ThingsListView({
collection: thingsList
});
Are you sure your collection isn't resorting itself? keep in mind that the order of the models in the collection won't change the order of how they appear on the page if they are already rendered.
I'm guessing that what you are trying to do is resort the items that have already been rendered, to do so you would need re-render your collection. If you are going to do so I would recommend that you cache your views and on a sort detach the associated element from the DOM and reattach them in the correct order.
As an example
var ThingsListView = Backbone.View.extend({
_views: {},
initialize: function () {
this.collection.bind('add', this.add, this);
this.collection.bind('reset', this.render, this); //sort triggers a reset
},
add: function (thing) {
var view = new ThingView({model: thing});
this._views[thing.cid] = view; //use client id of model as key for the views cache
this.$el.append(view.render().el);
},
render: function() {
$('li, this.$el).detach(); //detach so that bound events aren't lost
_.each(this.collection.models, function(thing) {
this.$el.append(this.views[thing.cid].el); //get view from cache
},this);
},
sort: function() {
this.collection.sort();
}
}
})
(a couple of differences from my example code and yours I'm assuming here that the collection view has a 'el' referring to a container 'ul', I also don't show how your triggering the sort (basically something like thingListView.sort();)
Edit: It might not be so obvious from the example code I posted, so I should have mentioned to begin with what #Deeptechtons said that when you sort a collection it triggers a reset event
Edit2: If your not interested in caching your views, then the easiest way to remove your current views would probablly be to add a class to the rendered div
var ThingView = Backbone.View.extend({
className: 'thingClass',
//rest of your thingViewCode
Then in your clearRender method just add $(".thingClass", this.$el).remove(); to the beginning of the method.
I am trying to get a bit of content to load on pageLoad instead of as a clickable event (but still keep the clickable event on the menu).
Here's the actionscript:
import mx.utils.Delegate;
/**
* This is the menu that comes up at the bottom with various analysis and navigation options when a thumbnail is selected.
*/
class imagegal.BottomMenu extends MovieClip
{
public var bg_mc:MovieClip;
public var btns1_mc:MovieClip;
public function BottomMenu()
{
btns1_mc.prev_mc.label_txt.text = "PREVIOUS IMAGE"
btns1_mc.next_mc.label_txt.text = "NEXT IMAGE";
btns1_mc.info_mc.label_txt.text = "INFO";
btns1_mc.thumbs_mc.label_txt.text = "THUMBNAILS";
btns1_mc.menu_mc.label_txt.text = "MAIN MENU";
btns1_mc.prev_mc.onRelease = Delegate.create(this, function() {
_parent.loadPrevious();
});
btns1_mc.next_mc.onRelease = Delegate.create(this, function() {
_parent.loadNext();
});
btns1_mc.info_mc.onRelease = Delegate.create(this, function() {
_parent.toggleInfo();
});
btns1_mc.thumbs_mc.onRelease = Delegate.create(this, function() {
_parent.showThumbs();
});
btns1_mc.menu_mc.onRelease = Delegate.create(this, function() {
_parent.showMenu();
});
}
public function resize(w:Number) {
//mask_mc._width = w;
//bg_mc._width = w;
}
}
The _parent.toggleInfo() function has the content i want displayed. My question is, when this the corresponding as file is fired by the SWF, can i have that function fire?
You could add an eventlistener to the image of which you want to display the blob, or use this.onLoad to add the eventlistener.