Removing eventhandlers not working: "this" context lost in Javascript? - javascript

I have a problem with removing the eventhandlers for a slider element
If I don't use proxy for the handler, the "this" will point to the dom element
How do I remove the handler?
Relevant code:
var slider = (function (slider) {
var Sliderhandle = function(handle){
EvtTarget.call(this);
this.events = {
start: ['touchstart', 'mousedown'],
move: ['touchmove', 'mousemove'],
end: ['touchend', 'touchcancel', 'mouseup']
};
this.options = {};
this.element = $$('div.ui-slider');
// set context for event handlers
this.start = this.start.bind(this);
this.move = this.move.bind(this);
this.end = this.end.bind(this);
this.proxy = function(func){
var that = this;
return(function(){
return func.apply(that,arguments);
});
}
Object.defineProperty(this, "__el",{
value:handle
});
};
Sliderhandle.prototype = Object.create(EvtTarget.prototype,{
init : {
value:function(config){
this.container = $$('div.ui-slider');
this.track = this.container.getElementsByClassName('ui-slider-track')[0];
this.value = (config && config.value) || 1;
this.min = (config && config.min) || 1;
this.max = (config && config.value) || 1000;
this.change = (config && config.change) || null; // callback
this.addEvents("start");
this.setValue(this.value);
},
enumerable:true
},
addEvents : {
value:function(name){
var list = this.events[name],
handler = this[name],
all;
handler = this.proxy(handler);
for (all in list){
this.container.addEventListener(list[all], handler, false);
}
},
enumerable:true
},
removeEvents:{
value:function(name){
var list = this.events[name],
handler = this[name],
all;
//handler = this.proxy(handler);
for (all in list){
this.container.removeEventListener(list[all], handler, false);
}
},
enumerable:true
},

The problem is because there's no event listener same with handler which passed to removeEventListener. this.proxy() generates new function for each call. It returns whole different function object even if you call it with same parameter, although the returned function will do semantically same job.
You should store proxy functions when add, then use that one when remove like this:
var Sliderhandle = function(handle){
// ....
this.proxyHandler = {};
}
// ....
addEvents : {
value: function(name){
var list = this.events[name],
handler = this[name],
all;
this.proxyHandler[name] = handler = this.proxy(handler);
for (all in list){
this.container.addEventListener(list[all], handler, false);
}
},
enumerable:true
},
removeEvents:{
value:function(name){
var list = this.events[name],
handler = this.proxyHandler[name],
all;
for (all in list){
this.container.removeEventListener(list[all], handler, false);
}
delete this.proxyHandler[name];
},
enumerable:true
},

Related

How to set highlight duration permanently

I want to set the duration time of the Ext.get(el).highlight() function permanently. Is this possible without using cls? I have already tried to give infinity to the duration config. However I receive many errors by doing that.
You need to override function highlight. There is constant 1000ms.
Ext.dom.Element.override({
highlight: function(color, options) {
var me = this,
dom = me.dom,
from = {},
animFly = new Ext.dom.Fly(),
restore, to, attr, lns, event, fn;
options = options || {};
lns = options.listeners || {};
attr = options.attr || 'backgroundColor';
from[attr] = color || 'ffff9c';
if (!options.to) {
to = {};
to[attr] = options.endColor || me.getColor(attr, 'ffffff', '');
}
else {
to = options.to;
}
// Don't apply directly on lns, since we reference it in our own callbacks below
options.listeners = Ext.apply(Ext.apply({}, lns), {
beforeanimate: function() {
// Reattach to the DOM in case the caller animated a Fly
// in which case the dom reference will have changed by now.
animFly.attach(dom);
restore = dom.style[attr];
animFly.clearOpacity();
animFly.show();
event = lns.beforeanimate;
if (event) {
fn = event.fn || event;
return fn.apply(event.scope || lns.scope || WIN, arguments);
}
},
afteranimate: function() {
if (dom) {
dom.style[attr] = restore;
}
event = lns.afteranimate;
if (event) {
fn = event.fn || event;
fn.apply(event.scope || lns.scope || WIN, arguments);
}
}
});
me.animate(Ext.apply({}, options, {
duration: 10000, // here new duration ms
easing: 'ease-in',
from: from,
to: to
}));
return me;
}
});
Or you can attach config to highlight method like :
Ext.get(el).highlight("#ffff9c", {
attr: "backgroundColor",
endColor: "#ffffff",
easing: 'easeIn',
duration: 4000
});

ng-click firing when selecting text or dragging

When using ng-click on a div:
<div ng-click="doSomething()">bla bla</div>
ng-click fires even if the user only selects or drags the div. How do I prevent that, while still enabling text selection?
In requiring something similar, where the usual text selection behavior is required on an element which should otherwise respond to ngClick, I wrote the following directive, which may be of use:
.directive('selectableText', function($window, $timeout) {
var i = 0;
return {
restrict: 'A',
priority: 1,
compile: function (tElem, tAttrs) {
var fn = '$$clickOnNoSelect' + i++,
_ngClick = tAttrs.ngClick;
tAttrs.ngClick = fn + '($event)';
return function(scope) {
var lastAnchorOffset, lastFocusOffset, timer;
scope[fn] = function(event) {
var selection = $window.getSelection(),
anchorOffset = selection.anchorOffset,
focusOffset = selection.focusOffset;
if(focusOffset - anchorOffset !== 0) {
if(!(lastAnchorOffset === anchorOffset && lastFocusOffset === focusOffset)) {
lastAnchorOffset = anchorOffset;
lastFocusOffset = focusOffset;
if(timer) {
$timeout.cancel(timer);
timer = null;
}
return;
}
}
lastAnchorOffset = null;
lastFocusOffset = null;
// delay invoking click so as to watch for user double-clicking
// to select words
timer = $timeout(function() {
scope.$eval(_ngClick, {$event: event});
timer = null;
}, 250);
};
};
}
};
});
Plunker: http://plnkr.co/edit/kkfXfiLvGXqNYs3N6fTz?p=preview
I had to deal with this, too, and came up with something much simpler. Basically you store the x-position on mousedown and then compare new x-position on mouseup:
ng-mousedown="setCurrentPos($event)"
ng-mouseup="doStuff($event)"
Function setCurrentPos():
var setCurrentPos = function($event) {
$scope.currentPos = $event.offsetX;
}
Function doStuff():
var doStuff = function ($event) {
if ($event.offsetX == $scope.currentPos) {
// Only do stuff here, when mouse was NOT dragged
} else {
// Do other stuff, which includes mouse dragging
}
}

Cocos2d-js sprite array touch event

var GameLayer = cc.Layer.extend({
ball:[],
number:[],
label:[],
numberofballs:8,
_order:0,
ctor:function () {
this._super();
this.init();
},
init:function(){
this._order=0;
cc.log(this._order);
this.setBall();
},
setCoordX:function(){
var coordX=Math.random()*1000%330+50;
cc.log(coordX);
return coordX;
},
setCoordY:function(){
var coordY = Math.random()*700%700+50;
cc.log(coordY);
return coordY;
},
onTouchBegan:function(touch, event){
var target = event.getCurrentTarget();
var PosInScreen = target.convertToNodeSpace(touch.getLocation());
var Size = target.getContentSize();
var rect = cc.rect(0, 0, Size.width, Size.height);
if(cc.rectContainsPoint(rect, PosInScreen)){
if(target.getTag()==this._order) {
target.removeFromParent();
this._order++;
}
}
return false;
},
setBall:function(){
for(var i=0;i<this.numberofballs;i++){
this.number.push(Math.round(Math.random()*100));
}
this.number.sort(function(left,right){
return left-right;
});
for(var i=0;i<this.numberofballs;i++){
var eventListener = cc.EventListener.create({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
onTouchBegan: this.onTouchBegan});
this.ball[i]=cc.Sprite.create(res.ball_png);
this.ball[i].x=this.setCoordX();
this.ball[i].y=this.setCoordY();
this.ball[i].setTag(i);
this.addChild(this.ball[i]);
var label=new cc.LabelTTF(this.number[i],"Ariel",25);
label.x=50;
label.y=55;
this.ball[i].addChild(label);
cc.eventManager.addListener(eventListener, this.ball[i]);
}
}
});
var GameScene = cc.Scene.extend({
onEnter:function () {
this._super();
var layer = new GameLayer();
this.addChild(layer);
}
});
This is my whole code.
First, in setBall function, I added
cc.eventManager.addListener(eventListener, this.ball[i]);
I want to make touch event with this.ball array directly.
Second,in onTouchBegan function, this._order is null.
I don't know why.
How can I fix it?
this._order is undefined because in onTouchBegan function this is instance of cc.EventListener class.
If You want to make this to be instance of GameLayer, You can bind it:
var eventListener = cc.EventListener.create({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
onTouchBegan: this.onTouchBegan.bind(this)});

javascript scoping in requirejs and qunit

I've been trying to puzzle out why the scoping was a little screwy on these 2 prototype objects - the first one is my preloader based on PreloadJS and the second is my QUnit test. I've fixed the issue but I accidently deleted the original thread which someone kindly responded to so I've reposted this working version to say thankyou to that person
'use strict';
define(['preloadjs'], function (PreloadJS) {
var Preloader = function (model) {
this.model = model;
this.totalLoaded = 0;
this.context = model.context;
this.isFinished = false;
this.onStart = (model.onStart != null) ? model.onStart : function (e) {};
this.onUpdate = (model.onUpdate != null) ? model.onUpdate : function (e) {};
this.onComplete = (model.onComplete != null) ? model.onComplete : function (e) {};
this.preload = null;
// make sure assets exists
if (model.assets == null) {
model.assets = {};
}
this.init();
};
/**
* initialise preloader
* #return {null}
*/
Preloader.prototype.init = function() {
var self = this;
var preload = new PreloadJS();
if (this.onStart != null) {
this.onStart();
}
preload.onProgress = function (e) { self.onUpdate(e); };
preload.onComplete = function (e) { self.handleComplete(e); };
preload.onFileLoad = function (e) { self.onFileLoad(e); };
preload.loadManifest(this.model.manifest);
this.preload = preload;
};
Preloader.prototype.handleComplete = function(e) {
this.isFinished = true;
this.onComplete(e);
};
/**
* called when each file in the manifest is loaded - if it's an image,
* create an image object and populate its properties
* #param {event} e event object
* #return {null}
*/
Preloader.prototype.onFileLoad = function(e) {
if (e.type == PreloadJS.IMAGE) {
var self = this;
var img = new Image();
img.src = e.src;
img.onload = function () { self.handleFileComplete(); };
this.model.assets[e.id] = img;
}
};
/**
* iterates totalLoaded when each image has intialised
* #return {null}
*/
Preloader.prototype.handleFileComplete = function() {
this.totalLoaded++;
};
// public interface
return Preloader;
});
and this is the preloader QUnit test
'use strict';
define(function (require) {
var Preloader = require('Preloader');
var manifest = [
{ src : '../app/images/splash-screen.jpg', id : 'splash-screen' }
];
var assets = {};
var startFired = 0;
var updateFired = 0;
var completeFired = 0;
var totalLoaded = 0;
var scopetest = 'test';
var loaded = 0;
var pl = new Preloader({
manifest : manifest,
assets : assets,
onStart : function (e) {
startFired++;
},
onUpdate : function (e) {
updateFired++;
totalLoaded = e.loaded;
},
onComplete : function (e) {
completeFired++;
// adding the unit tests into the onComplete function fixed the inconsistent test results i was getting
test('Preloader tests', function () {
expect(4);
equal(startFired, 1, 'onStart was called once exactly');
ok((updateFired > 0), 'onUpdate was called at least once. Total: ' + updateFired);
equal(completeFired, 1, 'onComplete was called once exactly');
notEqual(pl.model.assets['splash-screen'], undefined, 'there was at least one asset loaded: ' + pl.model.assets['splash-screen']);
});
QUnit.start();
}
});
QUnit.stop();
});
Thankyou for your response and apologies for deleting the thread
This question already answered in the OP - this is my acceptance

Javascript, problem with binding an event to a div-tag

i am trying to bind an event to a dynamically created div.
function GameField(playerNumber) {
this.fields = new Array();
this.player = playerNumber;
this.isPlayerActive = false;
this.currentRound = 0;
}
GameField.prototype.InitField = function(fieldNumber) {
var newField = document.createElement("div");
if (fieldNumber == 0 || fieldNumber == 6 || fieldNumber == 8 || fieldNumber == 17)
newField.className = 'gameCellSmall borderFull gameText gameTextAlign';
else
newField.className = 'gameCellSmall borderWithoutTop gameText gameTextAlign';
newField.onclick = function() { this.DivClick('a'); }
this.fields[fieldNumber] = newField;
return newField;
}
GameField.prototype.DivClick = function(fieldNumber) {
alert('Nummer: ' + fieldNumber);
}
Everything works perfectly, but when you click on one of the created divs, i end up with the following error message: Error: Object doesn't support this property or method.
If i replace the onclick function with this, then it works:
newField.onclick = function() { alert('Nummer: ' + fieldNumber); }
How can i get the onclick event to fire my DivClick function instead?
The problem is that the onclick event handler gets executed with the this value pointing to the DOM element that triggered the event, so that's why executing this.DivClick fails.
You need to enforce the context, in order to use instance methods within the event handler, for example, you could store a reference to the current instance:
GameField.prototype.InitField = function(fieldNumber) {
var newField = document.createElement("div");
//...
var instance = this;
newField.onclick = function() { instance.DivClick('a'); }
//...
}
That is the way
function DivClick (fieldNumber) {
alert('Nummer: ' + fieldNumber);
}
{ this.DivClick('a'); } - should be replaced to { DivClick('a'); }

Categories