I'm building an app in cocos2d-js, and I've got a problem with listening to keyboard events. I expect a method called setKeyboardEnabled to exists but when I call it i get an error that setKeyboardEnabled is not a function, Am I missing something?
var AnimationLayer = cc.Layer.extend({
ctor : function() {
this._super();
this.setKeyboardEnabled(true);
},
onKeyDown : function(key) {
cc.log("action");
}
...
...
)}
The same thing happens when I try to listen to touch events.
In Cocos2D v3 the way events are handled has changed. For a proper explanation of the whole system you should read New event manager in Cocos2d-Html5 v3.0.
In short, if you used to have:
var AnimationLayer = cc.Layer.extend({
ctor : function() {
this._super();
this.setKeyboardEnabled(true);
},
onKeyDown : function(key) {
cc.log("action");
}
...
...
)}
You have to turn it into something like:
var AnimationLayer = cc.Layer.extend({
ctor : function() {
this._super();
cc.eventManager.addListener({
event: cc.EventListener.KEYBOARD,
onKeyDown : function(key) {
cc.log("action");
}
}, this);
},
...
...
)}
This can sound a bit complicated at first, but this new mechanism allows you to attach these event listeners to any kind of cc node, not just layers. So for example you could do something like this to make a Sprite fade when the mouse is above it:
var hoverHandler = cc.EventListener.create({
event: cc.EventListener.MOUSE,
onMouseMove: function (event) {
var target = event.getCurrentTarget();
var locationInNode = target.convertToNodeSpace(event.getLocation());
var s = target.getContentSize();
var rect = cc.rect(0, 0, s.width, s.height);
if (cc.rectContainsPoint(rect, locationInNode)) {
cc.log("It's hovering! x = " + locationInNode.x + ", y = " + locationInNode.y);
target.opacity = 180;
return true;
} else {
target.opacity = 255;
return false;
}
}
});
var MyLayer = cc.Layer.extend({
init: function() {
this._super();
var mySpriteThatFadesWhenHovered = cc.Sprite.create(...);
this.addChild(mySpriteThatFadesWhenHovered);
cc.eventManager.addListener(hoverHandler.clone(), mySpriteThatFadesWhenHovered );
}
)}
Related
I have two objects defined, each one has an init function as follows
var TestSound = {
settings: {
testSoundFile : "static/main/persoonS",
postUrl : "dummy.com",
},
init : function(){
this.cacheDom();
this.bindEvents();
},
cacheDom: function(){
this.oneCertainButton = document.getElementById("buttonId");
this.soundElement = document.getElementById("sound");
},
bindEvents : function(){
that = this;
this.oneCertainButton.onclick = function(){ that.playSound(that.settings.testSoundFile); };
},
............
var CheckInput = {
settings: {
correctAnswer : "persoon",
},
init : function (){
this.cacheDom();
this.bindEvents();
},
cacheDom: function (){
this.submitButton = document.getElementById("submitButtonId");
this.answerInputBox = document.getElementById("answerId");
this.feedbackField = document.getElementById("feedbackId");
},
bindEvents: function(){
that = this;
this.submitButton.addEventListener("click", function(event){
..........
I wish to initialise them both (one after the other) with "window onload" like so:
window.onload = function() {
CheckInput.init.bind(CheckInput);
TestSound.init.bind(TestSound);
};
This doesn't work apparently as the init functions do not seem to be called.
However, it works if I initialise just one of the modules like so:
window.onload =
CheckInput.init.bind(CheckInput);
Any explanations on what is going wrong here much appreciated!
You want either
window.onload = function() {
CheckInput.init();
TestSound.init();
};
or
window.addEventListener("load", CheckInput.init.bind(CheckInput));
window.addEventListener("load", TestSound.init.bind(TestSound));
Any explanations on what is going wrong here much appreciated!
You have to create a function to be used as a handler that is called when the event fires. You can do that by using bind or with a function expression, but if you use both then only another function will be created when the event happens, and the init method is not called at all.
I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.
I have a one-window javascript application. I have a dashboard that displays certain images by loading via multiple get requests in the background.
Problem arises when not all get requests are finished on time and the context of the site changes because then I want to clear the dashboard. Yet if the get request havent't finished yet, they will populate the dashboard with the wrong images.
I am trying to think of a way to abort those get request. Can someone please direct me in the right direction?
var Dashboard = {
showAllAssets: function(){
var self = this;
this.resetDashboard();
$.get(this.urlForAllAssets, function(json){
self.loadAssets(json);
});
},
showAssetsForCategory: function(categoryId) {
...
},
getHtmlForAsset: function(id) {
var self = this;
$.get(this.urlForDashboardThumb + "/" + id.toString(), function(assetHtml){
var $asset = $(assetHtml);
self.insertAssetThumbIntoDom($asset);
// this gets inserted even when context changed, how can I prevent that?
var thumb = Object.create(Thumbnail);
thumb.init($asset);
}, 'html')
},
insertAssetThumbIntoDom: function($asset) {
$asset.appendTo(this.$el);
},
resetDashboard: function() {
this.$el.html("");
},
loadAssets: function(idList) {
var self = this;
var time = 200;
// These get requests will pile up in the background
$.each(idList, function(){
var asset = this;
setTimeout(function(){
self.getHtmlForAsset(asset.id);
}, time);
time += 200;
});
},
bind: function() {
$document.on('loadAssets', function(event, idList) {
self.loadAssets(idList);
});
$document.on('switched_to_category', function(event, categoryId) {
self.showAssetsForCategory(categoryId);
});
$document.on('show_all_assets', function(){
self.showAllAssets();
})
},
init: function($el) {
this.$el = $el;
this.resetDashboard();
this.bind();
}
}
Though you cant stop an already sent request, you can still solve your problem.
My solution is to generate a simple ID, a random set of numbers for example, and store somewhere in your dashboard, and send it along with the request and send it back with the image.
If a new context is generated, it will have a new ID.
If the image comes back with a different ID than the one in the current context, then discard it.
As pointed out by the comments, a possible solution is to store the current context and compare it within the success method on the get request.
I have changed my code insofar that now I'll store the current within the manager and also I pass the event around to the $.get-method.
This has the downside that the get requests are still processed though and the loading of the new context takes longer as those get requests are processed later if there are too many to process. I also dislike passing the event around.
var Dashboard = {
currentLoadEvent: null,
loadAssets: function(idList, event) {
var self = this;
$.each(idList, function(){
var asset = this;
self.getHtmlForAsset(asset.id, event);
});
},
getHtmlForAsset: function(id, event) {
var self = this;
$.get(this.urlForDashboardThumb + "/" + id.toString(), function(assetHtml){
if (event === self.currentLoadEvent) {
console.log('same event continuing');
var $asset = $(assetHtml);
self.insertAssetThumbIntoDom($asset);
var thumb = Object.create(Thumbnail);
thumb.init($asset);
} else {
console.log('context changed');
}
}, 'html')
},
bind: function() {
var self = this;
$document.on('loadAssets', function(event, idList) {
self.currentLoadEvent = event;
self.loadAssets(idList, event);
});
$document.on('switched_to_category', function(event, categoryId) {
self.currentLoadEvent = event;
self.showAssetsForCategory(categoryId, event);
});
$document.on('show_all_assets', function(event){
self.currentLoadEvent = event;
self.showAllAssets(event);
})
}
}
I created a different solution by storing the request in an array and aborting them when the context changed:
loadAssets: function(idList, event) {
var self = this;
var requests = [];
$.each(idList, function(){
var asset = this;
if (self.currentLoadEvent === event){
var request = $.get(self.urlForDashboardThumb + "/" + asset.id.toString(), function(assetHtml){
if (event === self.currentLoadEvent) {
var $asset = $(assetHtml);
self.insertAssetThumbIntoDom($asset);
var thumb = Object.create(Thumbnail);
thumb.init($asset);
console.log('completed get request');
} else {
console.log('context changed');
$.each(requests, function(){
this.abort();
console.log('aborted request');
})
}
}, 'html');
requests.push(request);
} else {
return false;
}
});
},
I've run into a strange problem with my jQuery code. I have a setup as follows:
(function($) {
var o = $({});
$.subscribe = function() { o.on.apply(o, arguments); };
$.publish = function() { o.trigger.apply(o, arguments); };
}(jQuery));
(function($) {
var CarrierView = {
subscriptions: function() {
$.subscribe( 'reporting.carrier.model.changed', this.renderModel );
},
renderModel: function( e, data ) {
var self = CarrierView;
}
};
var CarrierModel = {
subscriptions: function() {
$.subscribe( 'reporting.carrier.model.update', this.updateModel );
},
updateModel: function( value ) {
$.getJSON('carriers', function( data ) {
$.publish( 'reporting.carrier.model.changed', data );
});
}
};
window.CarrierView = CarrierView.init();
window.CarrierModel = CarrierModel.init();
})(jQuery);
Running a very basic pub/sub. My issue is the following:
A click event triggers the CarrierModel.updateModel method, which calls $.getJSON. The data returned is an Array[99], which is then published. When CarrierView.renderModel is called, the data there is the first element of the Array[99], an Array[5]. What am I doing incorrectly? How do I pass the whole set of data to the View?
I would like to know if someone knows how to make a function repeat over and over while the mouse is press, I don't know how to make it work. I know in prototype you can take events like
$('id').observe("click",function(event){})
$('id').observe("leave",function(event){})
$('id').observe("change",function(event){})
//etc...
but is something like $('id').observe("whilemousepress",function(event){}) :P
//I know there is not any event in Javascript but I would like to emulate.
I can't comment on the prototype specifics, but you could probably do this by creating an interval using setInterval() that is started on mousedown and is stopped using .clearInterval() on mouseup.
There is an event for when the mouse is down mousedown and one for when the mouse is up mouseup. Just start your events when the mouse is pressed and stop them when the button is released.
$('id').observe("mousedown",function(event){
// Do whatever you want
})
$('id').observe("mouseup",function(event){
// Stop the events starts when the mouse when down
})
ok... I guess both are correct what I did is :
$('right').observe('mousedown',function(event){ intervalRigth = setInterval(this.name + ".dosomething()",50); }.bind(this));
$('left').observe('mousedown',function(event){ intervalLeft = setInterval(this.name + ".dosomething()",50); }.bind(this));
$('right').observe('mouseup',function(event){ clearInterval(intervalRigth ); }.bind(this));
$('left').observe('mouseup',function(event){ clearInterval(intervalLeft ); }.bind(this));
//so I guess a mix of both answer are right thanks =)
var MouseUtils = (function() {
'use strict';
// VARIABLES
var _isScrolling = false;
var _buttonsArray = [false, false, false, false, false, false, false, false, false]; // 0 left, 2 right, 1 middle, other.. extra buttons, gaming mouses
var _mousePressed = false;
//EVENT LISTENERS
var _init = function(w, d) {
w.onscroll = function() {
_isScrolling = true;
};
d.onmousedown = function(e) {
_buttonsArray[e.button] = true;
_mousePressed = true;
};
d.onmouseup = function(e) {
_buttonsArray[e.button] = false;
_mousePressed = false;
};
d.oncontextmenu = function() { // this is mandatory for clicks down|ups works well
return false;
};
return this;
};
// TIMERS
var _scrollInterval = setInterval(function() {
if(_isScrolling) {
_isScrolling = false;
}
}, 500);
// EXPOSED
var _isLeftPressed = function() {
return _buttonsArray[0];
};
var _isRightPressed = function() {
return _buttonsArray[2];
};
var _isMiddlePressed = function() {
return _buttonsArray[1];
};
var _isScrolling = function() {
return _isScrolling;
};
var _clearScrollInterval = function() {
clearInterval(_scrollInterval);
};
return {
init: _init,
isLeftPressed: _isLeftPressed,
isRightPressed: _isRightPressed,
isMiddlePressed: _isMiddlePressed,
isScrolling: _isScrolling,
clearScrollInterval: _clearScrollInterval
};
})();
MouseUtils.init(window, document);
while(MouseUtils.isLeftPressed) {
// DO SOMETHING
}