Send event when module was executed - javascript

I'm really stuck on this.. I need to send an event when both Load module and Hide module code was executed, and only then send the event. Ideas on how to achieve this?
// Load module
(
function() {
var s=document.createElement('script');
s.type='text/javascript';
s.async=true;
s.src='https://example.com/bundles.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
)();
// Hide module
var inverval = setInterval(hideClass, 100);
function hideClass () {
if ($(".class").hide().length > 0) clearInterval(inverval);
}
// When both happend = Send a event to Google Analytics
DigitalData.push({'event':Module, 'eventLabel':'Page'});

If this is your only option, then perhaps there's something you are going about wrongly. Anyway, let's see ... Only when both events have taken place.
var HandleTwoEvents = function (key1, key2) {
this.count = 0;
this.pack = [];
$self = this;
this.startListening = function(fn) {
fn = fn || function () {}
window.addEventListener(key1, function (ev) {
if ($self.pack.indexOf(key1) < 0) {
$self.pack.push(key1);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key1, ev);
});
window.addEventListener(key2, function (ev) {
if ($self.pack.indexOf(key2) < 0) {
$self.pack.push(key2);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key2, ev);
});
}
}
Forgive me, i always use this function to create events
function createEvent(name, obj) {
var evt = document.createEvent("Event");
evt.initEvent(name, true, true);
evt.data = obj;
dispatchEvent(evt);
}
Now, to log both events ...
var both = new HandleTwoEvents("EventKeyOne", "EventKeyTwo");
both.startListening(function () {console.log("This means that both Events have taken place")});
Now, let's test ...
createEvent("EventKeyOne", {});
//key, data are the arguments ... function defined in startListening above does not execute, and upon inspection, both.count is seen to be 1
createEvent("EventKeyTwo", {});
//Now, function executes.
//It also works if "EventKeyTwo" is raised before "EventKeyOne"
Happy Coding!
PS: I'm sure there's a better way to handle the use of the $self variable, with some function binding, i guess. I've never been able to learn it.

Related

AngularJS: Listen to events, one after the other

is there an angular-way to listen for events that occur one after the other? For example, I want to listen on a $rootScope for the $routeChangeSuccess and the $viewContentLoaded event. When the first event occurs, and after that, the second event occurs, I want to call a callback.
Is this possible? Or do I have to write it on my own? It would also be nice to configure if the order of the events is important or not. Or any ideas, how to implement such a behaviour?
Update
Because I haven't found anything on the web, I came up with my own solution. I think it works, but I don't know if there are any drawbacks with this method.
And any suggestions how to integrate this global into an AngularJS project? Or even as a bower component? Should I attach the function to a scope, or to the rootScope? Any help is appreciated!
Here is the Plunker link and the code: http://plnkr.co/edit/slfvUlFCh7fAlE4IPt8o?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.events = [];
var successiveOn = function(events, eventScope, callback, orderImportant) {
// array for the remove listener callback
var removeListenerMethods = [];
// events array that is passed to the callback method
var eventsArr = [];
// how many events are fired
var eventCount = 0;
// how many events should be fired
var targetEventCount = events.length;
// track the next event, only for orderImportant==true
var nextEvent = events[0];
// iterate over all event strings
for (var i = 0; i < events.length; i++) {
var event = events[i];
// attach an $on listener, and store the remove listener function
var removeListener = eventScope.$on(event, function(evt) {
if (evt.name == nextEvent || !orderImportant) {
++eventCount;
nextEvent = events[eventCount];
eventsArr.push(evt);
// if all events has fired, call the callback method and reset
if (eventCount >= targetEventCount) {
callback(eventsArr);
nextEvent = events[0];
eventCount = 0;
eventsArr = [];
}
}
});
removeListenerMethods.push(removeListener);
}
// the return function is a anonymous function which calls all the removeListener methods
return function() {
for (var i = 0; i < removeListenerMethods.length; i++) {
removeListenerMethods[i]();
}
}
}
// order is unimportant
var removeListeners = successiveOn(["orderUnimportant1", "orderUnimportant2", "orderUnimportant3"], $scope, function(events) {
var str = "Events in order of trigger: ";
for (var i = 0; i < events.length; i++) {
str += events[i].name + ", ";
}
$scope.events.push(str);
}, false);
$scope.$broadcast("orderUnimportant1");
$scope.$broadcast("orderUnimportant2");
$scope.$broadcast("orderUnimportant3"); // Events were triggered 1st time
$scope.$broadcast("orderUnimportant3");
$scope.$broadcast("orderUnimportant2");
$scope.$broadcast("orderUnimportant1"); // Events were triggered 2nd time, order doesn't matter
removeListeners();
// order is important!
var removeListeners = successiveOn(["OrderImportant1", "OrderImportant2", "OrderImportant3"], $scope, function(events) {
var str = "Events in order of trigger: ";
for (var i = 0; i < events.length; i++) {
str += events[i].name + ", ";
}
$scope.events.push(str);
}, true);
$scope.$broadcast("OrderImportant1");
$scope.$broadcast("OrderImportant2");
$scope.$broadcast("OrderImportant3"); // Events were triggered
$scope.$broadcast("OrderImportant1");
$scope.$broadcast("OrderImportant3");
$scope.$broadcast("OrderImportant2"); // Events were NOT triggered
removeListeners();
});
Use the $q service aka the promise.
var routeChange = $q.defer();
var contentLoaded = $q.defer();
$rootScope.$on("$routeChangeSuccess", function() {
routeChange.resolve();
});
$rootScope.$on("$viewContentLoaded", function() {
contentLoaded.resolve();
});
$q.all([contentLoaded.promise, routeChange.promise]).then(function() {
//Fire your callback here
});
Specific Order:
routeChange.then(function() {
contentLoaded.then(function () {
//Fire callback
});
});
Without the nasty callback soup:
var routeChangeHandler = function() {
return routeChange.promise;
}
var contentLoadedHandler = function() {
return contentLoaded.promise
}
var callback = function() {
//Do cool stuff here
}
routeChangeHandler
.then(contentLoadedHandler)
.then(callback);
Thats pretty damn sexy...
More info on chained promises
I think Stens answer is the best way to tackle this if you want to do it with pure AngularJS facilities. However, often enough such cases indicate that you have to deal with more complex event stuf on a regular basis. If that's the case, I'd advice you to take a look at the Reactive Extensions and the rx.angular.js bridging library.
With the Reactive Extensions for JavaScript (RxJS) you can simplify the code to this:
Rx.Observable.combineLatest(
$rootScope.$eventToObservable('$routeChangeSuccess'),
$rootScope.$eventToObservable('$viewContentLoaded'),
Rx.helpers.noop
)
.subscribe(function(){
//do your stuff here
})

I want to not be able to run the same command twice very quickly

So I have this chunk of code here:
lockskipCommand = (function(_super) {
__extends(lockskipCommand, _super);
function lockskipCommand() {
return lockskipCommand.__super__.constructor.apply(this, arguments);
}
lockskipCommand.prototype.init = function() {
this.command = '/lockskip';
this.parseType = 'exact';
return this.rankPrivelege = 'bouncer';
};
lockskipCommand.prototype.functionality = function() {
data.lockBooth();
new ModerationForceSkipService();
return setTimeout((function() {
return data.unlockBooth();
}), 4500);
};
return lockskipCommand;
})(Command);
I want to be able to let it has some sort of cool down, so it can't be used quickly in a row. The reason I want this is to prevent from people being skipped, because that's what this chunk of code is for skipping people.
I hope this is enough information to get some help. Thanks!
You can use Underscore's debounce() method (with true as the third argument).
If you don't want to include Underscore for this simple task, you could do...
var debounceFn = function (fn, delay) {
var lastInvocationTime = Date.now();
delay = delay || 0;
return function () {
(Date.now() - delay > lastInvocationTime) && (lastInvocationTime = Date.now()) && fn && fn();;
};
};
jsFiddle.
What I need is a way to not be able to execute the command more than once in a row.
You could do something similar...
var onceFn = function (fn) {
var invoked = false;
return function () {
! invoked && (invoked = true) && fn && fn();
};
};
jsFiddle.

Custom Events in CLASS

I need to launch custom events from CLASS. I know to do this with DOM objects and jquery, using triggerHandler, like $(object)..triggerHandler("inputChange", {param:X});
The problem is when i try this with a Class, like this:
var MyClass = (function(){
var static_var = 1;
var MyClass = function () {
var privateVar;
var privateFn = function(){ alert('Im private!'); };
this.someProperty = 5;
this.someFunction = function () {
alert('Im public!');
};
this.say = function() {
alert('Num ' + this.someProperty);
$(this).triggerHandler("eventCustom");
}
this.alter = function() {
this.someProperty ++;
}
};
return MyClass;
})();
TheClass = new MyClass();
$(TheClass).on('eventCustom', function() {
alert('Event!');
});
TheClass.say();
This doesn't launch warnings or errors, but the events listener is not working (or event is not dispatched). I think the jQuery event system doesn't work with not DOM object, correct?
Any other way (I need events, not callbacks for my specific case) to launch the events?
Thanks a lot!
I wrote an ES6 event class for nowadays in under 100 lines of code without using JQuery. If you don't want to use DOM-events you can extend your class, which should deal with Events.
For listening to events, you can use on, once, onReady, onceReady. On is execute the callbackfunction every time the label is trigger. Once only one time. The "ready"-functions execute the callback, if the label had been already triggerd before.
For triggering an event, use a trigger. To remove an eventhandler, use off.
I hope the example makes it clear:
class ClassEventsES6 {
constructor() {
this.listeners = new Map();
this.onceListeners = new Map();
this.triggerdLabels = new Map();
}
// help-function for onReady and onceReady
// the callbackfunction will execute,
// if the label has already been triggerd with the last called parameters
_fCheckPast(label, callback) {
if (this.triggerdLabels.has(label)) {
callback(this.triggerdLabels.get(label));
return true;
} else {
return false;
}
}
// execute the callback everytime the label is trigger
on(label, callback, checkPast = false) {
this.listeners.has(label) || this.listeners.set(label, []);
this.listeners.get(label).push(callback);
if (checkPast)
this._fCheckPast(label, callback);
}
// execute the callback everytime the label is trigger
// check if the label had been already called
// and if so excute the callback immediately
onReady(label, callback) {
this.on(label, callback, true);
}
// execute the callback onetime the label is trigger
once(label, callback, checkPast = false) {
this.onceListeners.has(label) || this.onceListeners.set(label, []);
if (!(checkPast && this._fCheckPast(label, callback))) {
// label wurde nocht nicht aufgerufen und
// der callback in _fCheckPast nicht ausgeführt
this.onceListeners.get(label).push(callback);
}
}
// execute the callback onetime the label is trigger
// or execute the callback if the label had been called already
onceReady(label, callback) {
this.once(label, callback, true);
}
// remove the callback for a label
off(label, callback = true) {
if (callback === true) {
// remove listeners for all callbackfunctions
this.listeners.delete(label);
this.onceListeners.delete(label);
} else {
// remove listeners only with match callbackfunctions
let _off = (inListener) => {
let listeners = inListener.get(label);
if (listeners) {
inListener.set(label, listeners.filter((value) => !(value === callback)));
}
};
_off(this.listeners);
_off(this.onceListeners);
}
}
// trigger the event with the label
trigger(label, ...args) {
let res = false;
this.triggerdLabels.set(label, ...args); // save all triggerd labels for onready and onceready
let _trigger = (inListener, label, ...args) => {
let listeners = inListener.get(label);
if (listeners && listeners.length) {
listeners.forEach((listener) => {
listener(...args);
});
res = true;
}
};
_trigger(this.onceListeners, label, ...args);
_trigger(this.listeners, label, ...args);
this.onceListeners.delete(label); // callback for once executed, so delete it.
return res;
}
}
// +++ here starts the example +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class TestClassEvents extends ClassEventsES6 {
constructor() {
super();
this.once('sayHallo', this.fStartToTalk);
this.on('sayHallo', this.fSayHallo);
}
fStartToTalk() {
console.log('I start to talk... ');
}
fSayHallo(name = 'Nobody') {
console.log('Hallo ' + name);
}
}
let testClassEvents = new TestClassEvents();
testClassEvents.trigger('sayHallo', 'Tony');
testClassEvents.trigger('sayHallo', 'Tim');
testClassEvents.onReady('sayHallo', e => console.log('I already said hello to ' + e));
testClassEvents.trigger('sayHallo', 'Angie');
testClassEvents.off('sayHallo');
testClassEvents.trigger('sayHallo', 'Peter');
console.log('I dont say hallo to Peter, because the event is off!')
Your understanding of how javascript works is limited since you are approaching it from a traditional OOP point of view. Take a look at this fiddle http://jsfiddle.net/9pCmh/ & you will see that you can actually pass functions as variables to other functions. There are no classes in javascript, only functions which can be closures which can be made to emulate traditional classes:
var MyClass = (function(){
var static_var = 1;
var MyClass = function ( callback ) {
var privateVar;
var privateFn = function(){ alert('Im private!'); };
this.someProperty = 5;
this.someFunction = function () {
alert('Im public!');
};
this.say = function() {
alert('Num ' + this.someProperty);
callback();
}
this.alter = function() {
this.someProperty ++;
}
};
return MyClass;
})();
TheClass = new MyClass(function() {
alert('Event!');
});
TheClass.say();
Alternatively you could create a function in your "class" to configure the callback/trigger instead of passing it into the constructor.
Have a look at this as a start for your further reading on this concept... How do JavaScript closures work?
Edit
To appease those critics looking for an eventQueue here is an updated jsfiddle :)
http://jsfiddle.net/Qxtnd/9/
var events = new function() {
var _triggers = {};
this.on = function(event,callback) {
if(!_triggers[event])
_triggers[event] = [];
_triggers[event].push( callback );
}
this.triggerHandler = function(event,params) {
if( _triggers[event] ) {
for( i in _triggers[event] )
_triggers[event][i](params);
}
}
};
var MyClass = (function(){
var MyClass = function () {
this.say = function() {
alert('Num ' + this.someProperty);
events.triggerHandler('eventCustom');
}
};
return MyClass;
})();
TheClass = new MyClass();
events.on('eventCustom', function() {
alert('Event!');
});
events.on('eventCustom', function() {
alert('Another Event!');
});
TheClass.say();

Throttle event calls in jQuery

I have a keyup event bound to a function that takes about a quarter of a second to complete.
$("#search").keyup(function() {
//code that takes a little bit to complete
});
When a user types an entire word, or otherwise presses keys rapidly, the function will be called several times in succession and it will take a while for them all to complete.
Is there a way to throttle the event calls so that if there are several in rapid succession, it only triggers the one that was most recently called?
Take a look at jQuery Debounce.
$('#search').keyup($.debounce(function() {
// Will only execute 300ms after the last keypress.
}, 300));
Here is a potential solution that doesn't need a plugin. Use a boolean to decide whether to do the keyup callback, or skip over it.
var doingKeyup = false;
$('input').keyup(function(){
if(!doingKeyup){
doingKeyup=true;
// slow process happens here
doingKeyup=false;
}
});
You could also use the excellent Underscore/_ library.
Comments in Josh's answer, currently the most popular, debate whether you should really throttle the calls, or if a debouncer is what you want. The difference is a bit subtle, but Underscore has both: _.debounce(function, wait, [immediate]) and _.throttle(function, wait, [options]).
If you're not already using Underscore, check it out. It can make your JavaScript much cleaner, and is lightweight enough to give most library haters pause.
Here's a clean way of doing it with JQuery.
/* delayed onchange while typing jquery for text boxes widget
usage:
$("#SearchCriteria").delayedChange(function () {
DoMyAjaxSearch();
});
*/
(function ($) {
$.fn.delayedChange = function (options) {
var timer;
var o;
if (jQuery.isFunction(options)) {
o = { onChange: options };
}
else
o = options;
o = $.extend({}, $.fn.delayedChange.defaultOptions, o);
return this.each(function () {
var element = $(this);
element.keyup(function () {
clearTimeout(timer);
timer = setTimeout(function () {
var newVal = element.val();
newVal = $.trim(newVal);
if (element.delayedChange.oldVal != newVal) {
element.delayedChange.oldVal = newVal;
o.onChange.call(this);
}
}, o.delay);
});
});
};
$.fn.delayedChange.defaultOptions = {
delay: 1000,
onChange: function () { }
}
$.fn.delayedChange.oldVal = "";
})(jQuery);
Two small generic implementations of throttling approaches. (I prefer to do it through these simple functions rather than adding another jquery plugin)
Waits some time after last call
This one is useful when we don't want to call for example search function when user keeps typing the query
function throttle(time, func) {
if (!time || typeof time !== "number" || time < 0) {
return func;
}
var throttleTimer = 0;
return function() {
var args = arguments;
clearTimeout(throttleTimer);
throttleTimer = setTimeout(function() {
func.apply(null, args);
}, time);
}
}
Calls given function not more often than given amount of time
The following one is useful for flushing logs
function throttleInterval(time, func) {
if (!time || typeof time !== "number" || time < 0) {
return func;
}
var throttleTimer = null;
var lastState = null;
var eventCounter = 0;
var args = [];
return function() {
args = arguments;
eventCounter++;
if (!throttleTimer) {
throttleTimer = setInterval(function() {
if (eventCounter == lastState) {
clearInterval(throttleTimer);
throttleTimer = null;
return;
}
lastState = eventCounter;
func.apply(null, args);
}, time);
}
}
}
Usage is very simple:
The following one is waiting 2s after the last keystroke in the inputBox and then calls function which should be throttled.
$("#inputBox").on("input", throttle(2000, function(evt) {
myFunctionToThrottle(evt);
}));
Here is an example where you can test both: click (CodePen)
I came across this question reviewing changes to zurb-foundation. They've added their own method for debounce and throttling. It looks like it might be the same as the jquery-debounce #josh3736 mentioned in his answer.
From their website:
// Debounced button click handler
$('.button').on('click', Foundation.utils.debounce(function(e){
// Handle Click
}, 300, true));
// Throttled resize function
$(document).on('resize', Foundation.utils.throttle(function(e){
// Do responsive stuff
}, 300));
Something like this seems simplest (no external libraries) for a quick solution (note coffeescript):
running = false
$(document).on 'keyup', '.some-class', (e) ->
return if running
running = true
$.ajax
type: 'POST',
url: $(this).data('url'),
data: $(this).parents('form').serialize(),
dataType: 'script',
success: (data) ->
running = false

How can I call any function in a chain of functions, without the chaining?

Sorry if my question wasn't clear enough. I'll put my code here...
var chain = {
'fn_1' : {
//fn_1 code here
chain.fn_2();},
'fn_2' : {
//fn_2 code here
chain.fn_3();}
...and so on
}
Let's say if i wana call chain.fn_1(), is there a way I can do that without calling chain.fn_2()?
What I can think of right now is a flag, but that would be alot of excess flags probably for each function. Do you guys have any ideas?
If the series of functions each call the next one you're correct, you'd need to have some sort of flag. In all likelihood, what would be best would be to modify your functions so that they return the reference to the object. Then you could chain like so:
var chain = {
'fn_1': function () {
// do something here.
return this;
},
'fn_2': function () {
// do something here.
return this;
},
'fn_3': function () {
// do something here.
return this;
}
};
// call the full chain:
chain.fn_1().fn_2().fn_3();
// call only the middle.
chain.fn_2();
g.d.d.c's answer is best, but if you can't modify the object for some reason, you could do this:
var _oldFn2 = chain.fn_2
chain.fn_2 = function() { return; };
chain.fn_1();
chain.fn_2 = _oldFn2;
var chain = {
fn : ['fn1', 'fn2', 'fn3'],
call : function(name) {
var i = 0, pos = -1, l = this.fn.length;
for(i = 0; i < l; i += 1) {
if(this.fn[i] == name) {
pos = i;
}
if(pos !== -1) {
this[this.fn[i]]();
}
}
},
fn1 : function() {
alert('fn1');
},
fn2 : function() {
alert('fn2');
},
};
chain.call('fn1'); //chain
chain.fn1(); //single

Categories

Resources