Define the order of execution of functions after event in javascript - javascript

Just, before reading, I have read about this thread: Order of execution of functions bound to an event in Javascript but its not helping. Actually,
I have an anonymous function, define like that:
<input type="button" name="blablabla" value="Send" onclick="javascript:blablabla">
So, this function is on a button, use to validate forms. As you can see, It's an anonymous function, and I don't have any access on this code. This function start when I click on it. Okay, I have understood that
But, this function is not totally full, and I want to add my own, with her own logic of check. So I want my checks first, and then call the anonymous function. Here is my code:
function check() {
console.log("debut de check");
var participant = document.getElementById("new_participant_name");
var participant1 = document.getElementById("new_participant2_name");
var participant2 = document.getElementById("new_participant3_name");
participant = participant.value;
participant1 = participant1.value;
participant2 = participant2.value;
var trois_participants = (participant2) ? true : false;
if (!participant1 || !participant)
{
console.log("pas de participant1 ou participant, sert à rien de gérer la suite");
//if the script come here, I want to stop processing, and don't want to call the anonymous function.
return ;
}
}
window.onload = function()
{
document.getElementById("InsertButton").addEventListener('click', function () {
check();
})};
So, I want to call my function (check) before the anonymous function, but, with the same event. I don't know if I am totally understable... thanks per avance
EDIT: Sorry guys, My code have a bug before, yes the code is inlined, I will try all of your solutions tomorrow, thanks guys

If (and only if) the existing handler is attached using an inline onclick="..." handler, you can obtain its value, and then overwrite it:
window.onload = function() {
var el = document.getElementById('InsertButton');
var old_click = el.onclick;
el.onclick = undefined;
el.addEventListener('click', function() {
check();
old_click(this);
});
}

Why not create your own handler??
Element.prototype.myEventListener=function(name,func){
this.addEventListener(name,function(){
if(!check()){return;}
func();
});
};
Now you can do:
document.body.myEventListener("click",function(){
alert("t");
});
Check will always be called before the registered handler.
Note, to block the call, check must return false:
function check(){
return false;//no custom eventlistener fires
return true;//all will fire
}

Use the useCapture flag so you can intercept the event while it's travelling down to the button.
At that point you can perform your check, and if it fails you can call stopPropagation on the event to prevent it from reaching the handlers that are attached to its bubbling phase.
Also, by nature, events are quite bad at managing the order of execution. In general they depend on the order of registration of the listeners.
// code over which you have no control and can't change
var btn = document.getElementById("greeter");
btn.addEventListener("click", function() {
console.log("hello");
})
// code you can add later
function check() {
return Math.random() > 0.5;
}
window.addEventListener("click", function(e) {
var greeter = document.getElementById("greeter");
if (e.target === greeter && !check()) {
e.stopPropagation();
}
}, true)
<button id="greeter">hello world</button>

Related

How is the parameter involved in order to let function one "know" that it shall react to what happens in function two?

The following code actually works, but I don't understand why. How come that when I pass the "event"-parameter to the function zaehle(), the function actually "knows" that it is supposed to react on what happens in the setup function?
I just can't see what connnects the zaehle() and the setup() function or how the parameter that I pass to zaehle() would be involved.
I hope I could make the question clear. If not I'll gladly try to explain it somehow else. It really bugs me and I feel like I can't go on studying until I get it.
<body>
<div id="eins">0</div>
<div id="zwei">0</div>
<div id="drei">0</div>
<div id="vier">0</div>
<div id="funf">0</div>
</body>
JS
var mouseoverZaehler = 0;
function zaehle(event) {
mouseoverZaehler++;
event.target.innerHTML = mouseoverZaehler;
}
function setup() {
document.getElementById("eins").addEventListener("mouseover", zaehle);
document.getElementById("zwei").addEventListener("mouseover", zaehle);
document.getElementById("drei").addEventListener("mouseover", zaehle);
document.getElementById("vier").addEventListener("mouseover", zaehle);
document.getElementById("funf").addEventListener("mouseover", zaehle);
}
window.addEventListener("load", setup);
Here is what happens step by step:
Page loads
setup function is called (because of window.addEventListener("load", setup))
Each element in setup function gets a mouseover event listener attached to it and when it fires zaehle function is called (because of document.getElementById("number").addEventListener("mouseover", zaehle))
You move your mouse over any of the elements
zaehle function gets called - mouseoverZaehler is incremented and innerHTML of the targeted element is set to the updated value of mouseoverZaehler
Check out addEventListener docs for further details.
The addEventListener calls in your setup function tell the browser that when a mouseover event occurs on the relevant element, it should call the function you're giving it (zaehle, in your case). It's the browser that passes the argument to zaehle, later, when calling it.
You could imagine addEventListener, conceptually, as putting that handler function on a list for the event on the element:
// VERY conceptual, leaves out a lot of details
function addEventListener(eventName, handler) {
this.events[eventName].handlers.push(handler);
}
...and then later, when the event occurs, the browser creates an event object and calls those handlers:
// Again, VERY conceptual, leaves out a lot of details
var event = /*...*/;
element.events[eventName].handlers.forEach(function(handler) {
handler.call(element, event);
});
Here's a working analogue of what's going on:
function FakeElement () {
this.events = Object.create(null);
}
FakeElement.prototype.addEventListener = function(eventName, handler) {
var eventEntry = this.events[eventName];
if (!eventEntry) {
eventEntry = this.events[eventName] = {
handlers: []
};
}
eventEntry.handlers.push(handler);
};
FakeElement.prototype.trigger = function(eventName) {
var event = {type: eventName}; // "Browser" creates the event
var eventEntry = this.events[eventName];
var handlers = eventEntry && eventEntry.handlers;
if (handlers) {
handlers.forEach(function(handler) {
handler.call(this, event); // "Browser" calls handler, passing
}); // the event into it
}
};
// Using it:
function zaehle(event) {
console.log("zaehle got event: " + event.type);
}
var e = new FakeElement();
e.addEventListener("mouseover", zaehle);
console.log("added handler for mouseover to element");
// Simulate the event occurring
var timer = setInterval(function() {
e.trigger("mouseover");
}, 500);
setTimeout(function() {
clearInterval(timer);
}, 3000);
You have registered your callback/function zaehle() for mouseover event. So when that event occurs for a specific div, browser calls the callback with event object which contains information about the event and the target i.e event occurred on which element.

How to show the result of this jQuery function without the need of clicking the button?

I have this function below, however I want to make it work on windows load and show the result without clicking the button.
This is the code I use https://raw.githubusercontent.com/SuyashMShepHertz/indexedDB_sample/master/index.html
How to do this?
$("#getBtn").click(function(){
var type = 'permanent';
var request = db.transaction(["hashes"],"readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event){
$("#result").html("Name : "+request.result.name);
};
});
just put your code in
$( window ).load(function() {
//Code Here
});
If you need it both on click and initially when the page loads, make it a reusable function:
function doTheThing() {
var type = 'permanent';
var request = db.transaction(["hashes"], "readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event) {
$("#result").html("Name : " + request.result.name);
};
}
Then call it from both places you need it:
On page load
On click
To call it on page load, just make sure your script is at the end of the HTML (just before the closing </body> tag; this is best practice unless you have a good reason for doing something else) and call it:
doTheThing();
If you can't put the script at the end of the HTML, you can use jQuery's ready callback instead:
// Concise, but easy to misunderstand:
$(doTheThing);
// Or more verbose but also more clear:
$(document).ready(doTheThing);
(See note below about doing it directly or indirectly.)
To call it on click, hook it up, either directly or indirectly:
// Directly
$("#getBtn").click(doTheThing);
// Or indirectly
$("#getBtn").click(function() {
doTheThing();
});
The only reason for hooking it up indirectly would be to avoid having it receive the event object jQuery will pass it automatically, and to avoid having its return value examined by jQuery to see if it should stop propagation and prevent the default event action.
To avoid creating globals, I'd make sure the entire thing is in a scoping function:
(function() {
function doTheThing() {
var type = 'permanent';
var request = db.transaction(["hashes"], "readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event) {
$("#result").html("Name : " + request.result.name);
};
}
doTheThing();
$("#getBtn").click(doTheThing);
})();
just put it in $(document).ready, like this
$(document).ready(function(){
var type = 'permanent';
var request = db.transaction(["hashes"],"readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event){
$("#result").html("Name : "+request.result.name);
};
});

How to "scope" event listeners?

This is my code:
var myFunction(){
document.addEventListener("deviceready", self.onDeviceReady, false); // scoped event listener
function onDeviceReady() {
// ...
}
}
As far as I see when the event (deviceready) is triggered, the local (to myFunction) callback is run.
However the event listeners is global so another function with the same with global scope may be called as well, once the event is triggered.
How to make not only the callback, but the listener itself locally scoped to a function (I know how to do it for a DOM element but that's different)?
Elements themselves are scoped globally. As soon as you attach something to them, they are scoped that way. Consider the following ::
function myfunc(){
document.getElementById('someId').something = 'test';
}
This will now be accessed everywhere.
I see what you want to get at so you could try several things. Here is something you could try. This will check to see if an event already exists for an element and not let you add another.
var Marvel = {
on : function(element, action, callback){
Marvel.listeners = Marvel.listeners || {};
Marvel.listeners[action] = Marvel.listeners[action] || [];
for(var index in Marvel.listeners[action]){
if(Marvel.listeners[action][index].element == element){
return console.error("A '"+action+"' event is already established for:", element, Marvel.listeners[action][index]), false
}
}
element.addEventListener(action,callback);
Marvel.listeners[action].push({ element: element, callback: callback });
},
off: function(element, action){
if(!Marvel.listeners || !Marvel.listeners[action])
return console.error("off: No '"+action+"' listener has been created. Listeners: ", Marvel.listeners || 'none'), false;
for(var index in Marvel.listeners[action]){
if(Marvel.listeners[action][index].element == element){
element.removeEventListener(action, Marvel.listeners[action][index].callback);
Marvel.listeners[action].splice(index, 1);
return true
}
}
return console.error("A '"+action+"' event has not yet been established for:", element, Marvel.listeners[action]), false;
}
}
Usage:
Marvel.on(document, click, function(e){
console.log(e.target);
});
Marvel.off(document, click);
This sets an event listener to be stored in a separate object and checks to see if it already exists when adding new ones, and additionally allows the capability to have an off function to turn them off.
You can find the full explanation for this on my site in my profile in the Javascript Tracking Events section.
Your code should be like this :
function myFunction() {
document.addEventListener("deviceready", function () {
//...
}, false); // scoped event listener
}

Listen for a function to be fired?

this is more of an academic question than a problem I'm having.
I've written a function that sets up a click handler for Google Analytics Event Tracking. It starts off by removing any previous click handlers using a namespace so as not to interfere with other click handlers that may be applied. Using jQuery's $.each() it basically loops through the IDs in an array, applies the category,action and label as data-* attributes and a class to reference it by later.
It then uses the data attributes set previously to set up a click handler which in turn fires the Google Analytics Event Tracking.
My question is basically, can I bind this function to an .unbind() or .off() method that targets all click handlers? (not namespaced ones)
So if someone else unbinds all click handlers on a button that the Event Tracking code is applied to, the setupEventHandlers() function is run automatically and reapplies the lost GA Event Tracking code?
UPDATED QUESTION
My original question might have been unclear in what I was trying to achieve.
So there is a button in the DOM.
<input type='submit' value='Confirm' id='confirm-btn'>
setupEventHandlers() runs as soon as the DOM is ready, and applies an event listener to #confirm-btn, which is listening for the click event. Another function randomFunction() decides that it needs to remove all event listeners from #confirm-btn, using the $.off() method. I would like to be able to allow randomFunction() to remove all event listeners, as it may not have a namespace (for reason x), and then run setupEventHandlers() again once $.off() has finished.
Consider that I can't directly edit the HTML because it's used across multiple sites, and the tracking code is for a single instance.
function setupEventHandlers(){
var buttonsToTrack = [['#buttonId','category','action', 'label'],
['#buttonId2','category2','action2', 'label2'],
['#buttonId3','category3','action3', 'label3']]
$('.js-buttonTracker').off('click.eventTrackingHandler');
$.each(buttonsToTrack, function(index, buttonDetails){
if(document.querySelector(buttonDetails[0]) != null){
$(buttonDetails[0]).attr('data-category', buttonDetails[1]);
$(buttonDetails[0]).attr('data-action', buttonDetails[2]);
$(buttonDetails[0]).attr('data-label', buttonDetails[3]);
$(buttonDetails[0]).addClass('js-buttonTracker');
}
});
$('.js-buttonTracker').on('click.eventTrackingHandler', function(){
_gaq.push(['_trackEvent', $(this).attr('data-category'), $(this).attr('data-action'), $(this).attr('data-label')]);
console.log($(this).attr('data-category') + ' ' + $(this).attr('data-action'), $(this).attr('data-label'));
});
}
// Save the original .off in a variable
(function(jq_off, recursive) {
// Redefine .off
$.fn.off = function(event, selector, function) {
var save_recursive = recursive;
recursive = true;
// Then call the original $.off
var return_val = jq_off.apply(this, arguments);
recursive = save_recursive;
if (!recursive) {
setupEventHandlers();
}
return return_val;
};
)($.fn.off, false);
You don't need to redefine .unbind, as it just calls .off.
My advise to you is to use data- tag elements and assign them a click listener independent of other functionalities, for example :
HTML:
<a href="http://google.com" data-toggle="GAPush" data-event="_trackEvent" data-action="google"....>..</a>
JS:
$('[data-toggle="GAPush"]').click(function() {
var eventname = $(this).data('name');
var action = $(this).data('action');
//do your login here and read other parameters from data-eventname m use _gapush()
});
you know you can have unlimited data- tag elements if you need them for other functionalities.
Try (this pattern)
Edit
v2
It should be possible to utilize setInterval to "poll" for the event namespace being attached, or not , to the target elements
window.setInterval(function() {
$._data($(".js-buttonTracker")[0], "events") != undefined &&
$._data($(".js-buttonTracker")[0], "events")
.click[0].namespace === "eventTrackingHandler"
? $.noop()
: setupEventHandlers()
}, 1000);
v1
html
<button>1</button>
<button>2</button>
<button>3</button>
<!-- `data`, `em` for `event` status notifications at jsfiddle -->
<data></data>
<em></em>
js
$(function () {
setupEventHandlers = function (status) {
if (status === "on") {
// if `events` `off`, turn back `on` by calling `_events()`
_events();
$(window).trigger("mousemove.check");
} else {
$("data").html(status);
};
};
eventsCheck = function () {
$(window).on("mousemove.check", function (e) {
// `target` `elements` ;
// e.g., `.js-buttonTracker`
var el = $("button");
// if `events` attached to `el`,
// check `events` `namespace`
if ($._data($(el)[0], "events") != undefined) {
$.each($._data($(el)[0], "events").click, function (k, v) {
if (v.namespace != "eventTrackingHandler") {
setupEventHandlers("attach `eventTrackingHandler`"
+ " namespace called");
} else {
$("data").html("`eventTrackingHandler`"
+ " namespace attached");
};
});
} else {
// if `events` _not_ attached to `el`,
// turn `events` back `on` by calling
// `setupEventHandlers("on")`
$("data").html("`eventTrackingHandler` namespace `off` ,"
+ " turning back `on` in 1 seconds");
// `setTimeout()` utilized here to show operability,
// alternatively, could call `setupEventHandlers("on")`
// without delay
setTimeout(function () {
setupEventHandlers("on");
}, 1000);
};
});
};
eventsCheck();
_events = function () {
$("button").on("click.eventTrackingHandler", function (e) {
$("em").html("clicked at " + $.now());
// turn `off` `events` for `button`'s when `button`
// having `text` of `"3"` `click`ed,
// simulating the condition
// "if someone else unbinds all click handlers on a button
// that the Event Tracking code is applied to" ,
// "observed" by `eventsCheck()` ,
// which calls `_events()` within `setTimeout()`
if ($(e.target).text() === "3") {
$("button").off("click.eventTrackingHandler");
};
});
};
_events();
});
jsfiddle http://jsfiddle.net/guest271314/z5u97/

Transform any JavaScript function into a page event

I need to be able to achieve the following (one way or another):
function ShowContent() {}
document.onShowContent = function ()
{
// anything I want to happen....
}
What I'm trying to do is to add a kind of listener to me Advertisement code on the page that will auto refresh the ad slot when a specific function is called. Instead of having that function "ShowContent()" directly refresh the ad code, I want the ad code to refresh if it detects that "ShowContent()" has been called.
Thanks.
Modern javascript libraries make this easy. You can do it "by hand" of course, but here's a quick example with jQuery
First, the listener
$(document).bind( 'ShowContent', function()
{
// anything you want
});
Then the trigger
$(document).trigger( 'ShowContent' );
You could even go this route if you want
function ShowContent()
{
$(document).trigger( 'ShowContent' );
}
Here is a quick sample i threw together
var ev = (function(){
var events = {};
return {
on: function(name, handler){
var listeners = (name in events) ? events[name] : (events[name] = []);
listeners.push(handler);
},
raise: function(name){
var listeners = events[name];
if (listeners) {
var i = listeners.length;
while (i--) {
listeners[i]();
}
}
}
};
})();
// add a listener
ev.on("foo", function(){
alert("bar");
});
If you cannot manually alter the method in question to trigger the event, then you can 'wrap' it.
function methodIHaveNoControlOver(){
....
}
// intercept the call
var originalFn = methodIHaveNoControlOver;
// here we replace the FunctionDeclaration with a FunctionExpression containing a reference to the original FunctionDeclaration
methodIHaveNoControlOver = function(){
originalFn();
ev.raise("foo");
};
But note that this will not work if methodIHaveNoControlOver uses this to reference anything; so that will require more work.

Categories