Javascript removeEventListner inside the callback? - javascript

How to addEventListener which will be triggered only one time + passing arguments to it?
function () {
var params = ...
elem.addEventListener('load', function(event, params) {
elem.removeEventListner('load', ...);
do_something(params)
}
}

when using addEventListener, you can provide a config object as a second argument, where you can specify that it should run once (like the code below). Regarding passing an extra argument to your event callback, you can use the method bind by providing the context and the extra argument
var button = document.querySelector('#btn');
button.addEventListener('click', function (params, ev) {
console.log(params);
}.bind(button, { name: 'test'}), {
once: true
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<button id="btn">click</button>
</body>
</html>

If you name your anonymous function you can reference it inside:
function () {
var params = ...
elem.addEventListener('load', function callback(event, params) {
elem.removeEventListner('load', callback);
do_something(params)
}
}
It wont leak to the global scope so you can name all of your 'one-time' events callback.

The removeEventListener method requires you to specify which function you're removing. That means you need a reference to that function so you can pass it to both addEventListener and removeEventListener. Something like this:
function bindListener(elem, params) {
var listener = function() {
do_something(params);
elem.removeEventListener('load', listener);
}
elem.addEventListener('load',listener);
}

Related

Managing service callbacks from controller using promises in AngularJS?

I have to manage the callback when calling a service function from a controller. My idea is to wrap the service functionality in a promise but then I can't reference the service function from the controller directly. Instead I have to create another function to handle the view events.
function exampleSrv($q) {
this.exampleFn = function() {
var q = $q.defer();
// Do something
q.resolve();
return q.promise;
};
}
function exampleCtrl(exampleSrv) {
this.exampleFn = exampleSrv.exampleFn;
/* This works but I want to avoid this if possible
this.clickHandler = function() {
this.exampleFn()
.then(function() {
console.log('yay');
})
};
*/
/* And instead do something like this but as a reference not as a call
this.exampleFn()
.then(function() {
console.log('yay');
})
*/
}
Is there a better approach to do this?
Example:
http://plnkr.co/edit/jg5yoC?p=info
In short, no, there's no better approach to this. In fact this is the advised manner to tackle such problems.
Actually, you can try something like this: (I am having plunker issues otherwise would have created one)
// Example Service
function exampleSrv($q) {
this.exampleFn = function() {
var q = $q.defer();
// Do something
q.resolve();
return q.promise.then(function() {
return {
"data": "12345"
};
});
};
}
// Example Controller
function exampleCtrl(exampleSrv) {
var ctrl = this;
exampleSrv.exampleFn().then(function(data){
ctrl.exampleFn = data;
});
/* This works but I want to avoid this
this.clickHandler = function() {
this.exampleFn()
.then(function() {
console.log('yay');
})
};
*/
/* And instead do something like this
this.exampleFn()
.then(function() {
console.log('yay');
})
*/
}
angular.module('example', [])
.service('exampleSrv', exampleSrv)
.controller('exampleCtrl', exampleCtrl);
Then in the HTML markup, you can do this:
<!DOCTYPE html>
<html ng-app="example">
<head>
<script data-require="angular.js#1.2.14" data-semver="1.2.14" src="http://code.angularjs.org/1.2.14/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="exampleCtrl as example">
<!-- bind value directly from service -->
{{example.exampleFn}}
</body>
</html>
This way, you don't need an extra controller function and can take service data directly to your markup. Hopefully this is what you were looking for. Good luck.

Pass function to a jquery key event?

I have a function foo(peram) which I want to call from multiple jquery .keyup() events.
How can I define/pass function foo so that I can call it from inside the event?
I tried something like this:
function foo(peram) {
alert(peram);
}
$("#someElement").keyup(function(alert) {
foo("You pressed a key!");
});
However I get TypeError: foo is not a function.
Update:
I have removed everything from my script and html, and it still does not work.
html:
<html>
<head>
<script src="../jquery-1.10.2.js" type="text/javascript"></script>
<script src="test.js" type="text/javascript"></script>
</head>
<body onload="asdf()">
<input type="text" name="name">
</body>
</html>
test.js:
function asdf() {
function hqxftg(stuff) {
alert(stuff);
}
$(document).ready(function() {
$('[name="name"]').keyup(function(hqxftg) {
alert(typeof hqxftg)
hqxftg("asdf");
})
})
}
It does seem to work in jsfiddle for some reason.
It is because you have named the event parameter same as the function
function asdf() {
function hqxftg(stuff) {
alert(stuff);
}
$(document).ready(function () {
$('[name="name"]').keyup(function (event) {
alert(typeof hqxftg)
hqxftg("asdf");
})
})
}
The event callback keyup receives the event object as the first parameter, you are naming it as hqxftg which overrides the external scoped function name.
Also there is no need to use the onload="", you can just use the dom ready callback
jQuery(function ($) {
function hqxftg(stuff) {
alert(stuff);
}
$('[name="name"]').keyup(function (event) {
alert(typeof hqxftg)
hqxftg("asdf");
})
})
You are missing a couple of things...
1) You miss the ; at the end of calling foo()
2) You are missing tags to close the jQuery selector
When you try this it will work:
function foo(peram) {
alert(peram);
}
$("#someElement").keyup(function(alert) {
foo("You pressed a key!");
});
JSFiddle here...
Update: Post has been updated and original comment of mine becomes obsolete.
I would go with the comment of Derek.
I am able to reproduce the problem: JSFiddle
Is it correct you have also foo declared as a var?
Try this code.
HTML
<input id="someElement" />
Java script:
function foo(peram) {
alert(peram);
}
$( document ).ready(function() {
$("#someElement").keyup(function() {
foo("You pressed a key!");
});
});
Demo

Why do I get this JavaScript ReferenceError

What am doing wrong. I try to make object but when i try to initialize i get this error in console: I try to put all in document.ready and whitout that but dont work. In both case i have some error. Am new sorry for dumb question
ReferenceError: Circle is not defined
var obj = new Circle;
JS
$(function(){
var Circle = {
init: function() {
console.log("Circle initialized");
}
};
});
HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="javascript/circle.js"></script>
<script>
$(document).ready(function(){
var obj = new Circle;
obj.init();
})
</script>
</head>
<body>
<div id="test" >TODO write content</div>
</body>
</html>
NEW UPDATE
$(function(){
window.Circle = {
init: function() {
console.log("Circle initialized");
}
};
window.Circle.init();
});
....
<head>
<script>
window.Circle().init();
</script>
</head>
You've defined your "Circle" function inside another function — the anonymous function you pass in as a a "ready" handler. Therefore, that symbol ("Circle") is private to that function, and not visible to the other code.
You can make it global like this:
window.Circle = {
// ...
};
You could also add it to the jQuery namespace (may or may not be appropriate; depends on what you're doing), or you could develop your own namespace for your application code. Or, finally, you could consider combining your jQuery "ready" code so that the "Circle" object and the code that uses it all appears in the same handler.
edit — another possibility is to move your "Circle" declaration completely out of the "ready" handler. If all you do is initialize that object, and your property values don't require any work that requires the DOM or other not-yet-available resources, you can just get rid of the $(function() { ... }) wrapper.
1) you are assigning Circle in a function context, not as a global. You can only use it there unless you expose it to global.
2) you are calling Circle as a constructor, but Circle is not a function.
This solves both issues:
var Circle = function () {};
Circle.prototype.init = function () {
console.log('Circle initialized.');
};
var obj = new Circle();
obj.init();

Using 'this' in nested function in class [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Nested function parameters and 'this' context in Javascript
I'm currently having problems to design my JS classes as desired due to a problem using 'this' in a nested class function call. Don't know how to describe it better so here's a sample what I mean.
test.html
<!DOCTYPE html>
<html class="main" lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript">
function doIt() {
var myTestClass = new TestClass();
}
</script>
</head>
<body>
<button onclick="doIt();">Do it!</button>
</body>
</html>
test.js
function TestClass() {
// this is working
this.alertValue('This works');
// this is not working
setTimeout(function(){this.alertValue('This does not work!')}, 1000);
}
TestClass.prototype.alertValue = function(value) {
alert('Value is: ' + value);
}
Of course this is just a simplified example to demonstrate the problem I mean.
So how could I use the 'this' identifier within the function inside the setTimeout call or how would be a better / the correct way to achieve that?
Thanks a lot for your help in advance!
Cheers
Save the value of this in a variable (self) and then you can access it within setTimeout.
function TestClass() {
this.alertValue('This works');
var self = this;
setTimeout(function() {
self.alertValue('This does not work!')
}, 1000);
}

Using Youtube's javascript API with jQuery

I'm currently trying to use the YouTube API as part of a jQuery plugin and I've run into a bit of a problem.
The way the YT api works is that you load the flash player and, when it's ready it will send a call back to a global function called onYouTubePlayerReady(playerId). You can then use that id combined with getElementById(playerId) to send javascript calls into the flash player (ie, player.playVideo();).
You can attach an event listener to the player with player.addEventListener('onStateChange', 'playerState'); which will send any state changes to another global function (in this case playerState).
The problem is I'm not sure how to associate a state change with a specific player. My jQuery plugin can happily attach more than one video to a selector and attach events to each one, but the moment a state actually changes I lose track of which player it happened in.
I'm hoping some example code may make things a little clearer. The below code should work fine in any html file.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="application/text+html;utf-8"/>
<title>Sandbox</title>
<link type="text/css" href="http://jqueryui.com/latest/themes/base/ui.all.css" rel="stylesheet" />
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("jquery", "1.3.2");
google.load("jqueryui", "1.7.0");
</script>
<script type="text/javascript" src="http://swfobject.googlecode.com/svn/tags/rc3/swfobject/src/swfobject.js"></script>
<script type="text/javascript">
(function($) {
$.fn.simplified = function() {
return this.each(function(i) {
var params = { allowScriptAccess: "always" };
var atts = { id: "ytplayer"+i };
$div = $('<div />').attr('id', "containerplayer"+i);
swfobject.embedSWF("http://www.youtube.com/v/QTQfGd3G6dg&enablejsapi=1&playerapiid=ytplayer"+i,
"containerplayer"+i, "425", "356", "8", null, null, params, atts);
$(this).append($div);
});
}
})(jQuery);
function onYouTubePlayerReady(playerId) {
var player = $('#'+playerId)[0];
player.addEventListener('onStateChange', 'playerState');
}
function playerState(state) {
console.log(state);
}
$(document).ready(function() {
$('.secondary').simplified();
});
</script>
</head>
<body>
<div id="container">
<div class="secondary">
</div>
<div class="secondary">
</div>
<div class="secondary">
</div>
<div class="secondary">
</div>
</div>
</body>
</html>
You'll see the console.log() outputtin information on the state changes, but, like I said, I don't know how to tell which player it's associated with.
Anyone have any thoughts on a way around this?
EDIT:
Sorry, I should also mentioned that I have tried wrapping the event call in a closure.
function onYouTubePlayerReady(playerId) {
var player = $('#'+playerId)[0];
player.addEventListener('onStateChange', function(state) {
return playerState(state, playerId, player); } );
}
function playerState(state, playerId, player) {
console.log(state);
console.log(playerId);
}
In this situation playerState never gets called. Which is extra frustrating.
Edit:
Apparently calling addEventListener on the player object causes the script to be used as a string in an XML property that's passed to the flash object - this rules out closures and the like, so it's time for an old-school ugly hack:
function onYouTubePlayerReady(playerId) {
var player = $('#'+playerId)[0];
player.addEventListener('onStateChange', '(function(state) { return playerState(state, "' + playerId + '"); })' );
}
function playerState(state, playerId) {
console.log(state);
console.log(playerId);
}
Tested & working!
Im Using Jquery SWFobject plugin, SWFobject
It is important to add &enablejsapi=1 at the end of video
HTML:
<div id="embedSWF"></div>
Jquery:
$('#embedSWF').flash({
swf: 'http://www.youtube.com/watch/v/siBoLc9vxac',
params: { allowScriptAccess: "always"},
flashvars: {enablejsapi: '1', autoplay: '0', allowScriptAccess: "always", id: 'ytPlayer' },
height: 450, width: 385 });
function onYouTubePlayerReady(playerId) {
$('#embedSWF').flash(function(){this.addEventListener("onStateChange", "onPlayerStateChange")});
}
function onPlayerStateChange(newState) {
alert(newState);
}
onYouTubePlayerReady must be outside of $(document).ready(function() to get fired
I had this same problem and tried the accepted answer. This didn't work for me; the playerState() function was never called. However, it put me on the right path. What I ended up doing was this:
// Within my mediaController "class"
window["dynamicYouTubeEventHandler" + playerID] = function(state) { onYouTubePlayerStateChange(state, playerID); }
embedElement.addEventListener("onStateChange", "dynamicYouTubeEventHandler" + playerID);
// End mediaController class
// Global YouTube event handler
function onYouTubePlayerStateChange(state, playerID) {
var mediaController = GetMediaControllerFromYouTubeEmbedID(playerID);
mediaController.OnYouTubePlayerStateChange(state);
}
It's fairly nasty, but so is the current state of the YouTube JavaScript API.
Here is some other helpful/nasty code if you are using any kind of advanced prototyping patterns. This basically allows you to retrieve a "class" instance from the YouTube player ID:
// Within my mediaController "class"
// The containerJQElement is the jQuery wrapped parent element of the player ID
// Its ID is the same as the player ID minus "YouTubeEmbed".
var _self = this;
containerJQElement.data('mediaController', _self);
// End mediaController class
// Global YouTube specific functions
function GetMediaControllerFromYouTubeEmbedID(embedID) {
var containerID = embedID.replace('YouTubeEmbed', '');
var containerJQObject = $("#" + containerID);
return containerJQObject.data('mediaController');
}
function onYouTubePlayerReady(playerId) {
var mediaController = GetMediaControllerFromYouTubeEmbedID(playerId);
mediaController.OnYouTubeAPIReady();
}
Here's a nice article that goes through creating a class to wrap an individual player, including dispatching events to individual objects using a similar approach to that mentioned in a previous answer.
http://blog.jcoglan.com/2008/05/22/dispatching-youtube-api-events-to-individual-javascript-objects/
How about something like so:
var closureFaker = function (func, scope) {
var functionName = 'closureFake_' + (((1+Math.random())*0x10000000)|0).toString(16);
window[functionName] = function () {
func.apply(scope || window, arguments);
};
console.log('created function:', functionName, window[functionName]);
return functionName;
};
ytplayer.addEventListener("onStateChange", closureFaker(function () {
//place your logic here
console.log('state change', arguments)
}));

Categories