Using a single DOM Content Loaded call - javascript

What would be the right approach to use a single DOMContentLoaded call on multiple functions as shown below:
Here is what I have:
function rtd3Transaction() {
if (rtd3tchanged.checked || rtd3dchanged.checked) {
formWrapperCertainSelection.style.display = '';
formWrapperConfirm.required = true;
} else {
formWrapperCertainSelection.style.display = 'none'
formWrapperConfirm.required = false;
}
}
rtd3tchanged.addEventListener('change', rtd3Transaction);
document.addEventListener('DOMContentLoaded', rtd3Transaction);
rtd3Transaction();
function rtd3Device() {
if (rtd3tchanged.checked || rtd3dchanged.checked) {
formWrapperCertainSelection.style.display = '';
formWrapperConfirm.required = true;
} else {
formWrapperCertainSelection.style.display = 'none'
formWrapperConfirm.required = false;
}
}
rtd3dchanged.addEventListener('change', rtd3Device);
document.addEventListener('DOMContentLoaded', rtd3Device);
rtd3Device();

It's fine to do it the way you have it now. You can attach multiple handlers to the event, they'll all get called.
If you want to change it, though, you can do it the same way you do any other time you want to do multiple things at once from a single place: you put the operations in a function, and arrange for that function to be called:
document.addEventListener('DOMContentLoaded', function() {
rtd3Transaction();
rtd3Device();
});

I prefer to create an array that I can push new initialization routines onto wherever they're defined, then initialize each once the content-load event fires.
const initRoutines = []
function example1() {}
initRoutines.push(example1)
function example2() {}
initRoutines.push(example2)
document.addEventListener("DOMContentLoaded", () => {
initRoutines.forEach(routine => routine())
})

Related

passing an array to a function but logs as undefined

I have 2 event listeners and they both push the element that is clicked into 2 separate arrays. These console.log in the event listener functions as expected, however when I pass them into a move function they both return undefined.
I've tried it with just passing the element that is selected into the function as well with the same result.
document.querySelectorAll('img').forEach(function(el) {
el.addEventListener('click' , function(params) {
el.classList.add('selected')
selectedPieces.push(el)
console.log(selectedPieces)
moveBy(selectedPieces)
})
})
document.querySelectorAll('.square').forEach(function(el) {
el.addEventListener('click', function() {
el.classList.add('target')
targetPieces.push(el)
console.log(targetPieces)
moveBy(targetPieces)
})
})
function moveBy(selected,target) {
console.log(selected)
console.log(target)
}
So I did create a work around in this. It involved sending the addEventListeners to their own functions, then adding them to an array, then sending that array to it's own function which then allowed me to move things around.
Only thing wrong with this version is that it only works one time, so I'm currently working through what is causing it to only fire once.
document.querySelectorAll('img').forEach(function(el) {
el.addEventListener('click' , addselected)
})
document.querySelectorAll('.square').forEach(function(el) {
el.addEventListener('click', addtarget)
})
function addselected(selected){
console.log('---addselected---')
var select = selected.path[0]
moveList.push(select)
}
function addtarget(target){
console.log('---addtarget---')
if (target.path.length === 7){
var tar = target.path[1]
}else{
var tar = target.path[0]
}
moveList.push(tar)
moveBy(...moveList)
}
function moveBy(...moveList) {
console.log('---moveBy---')
let moveTarg = moveList[2]
let moveSel = moveList[0]
if (typeof moveTarg === 'undefined') {
console.log('not working')
}else{
moveTarg.appendChild(moveSel)
moveList = []
}
console.log(moveList)
}
well
function moveBy(selected,target) {
console.log(selected)
console.log(target)
}
has two paramaters.
moveBy(targetPieces)
but you pass only one parameter in.

JQuery/Javascript: Getting locally changed booleans

I'm having some trouble with booleans and scope. I have set a global boolean to false, upon a function the boolean turns true. if the boolean is true I want another function to run. However the boolean is only changed in the scope of the function that makes it true and is still false globally. Thus the following function which needs the boolean to be true is not running.
The first part of my code looks something like this:
let threePick= false;
let twoPick = false;
let onePick = false;
let found = false;
var startGame = false;
function selector() {
$("#threebut").click(function () {
if (found === false) {
found = true;
onePick = true;
twoPick = true;
var input = this;
input.disabled = true;
}
})
$("#onebut").click(function () {
if (found === false) {
found = true;
threePick = true;
twoPick = true;
var input = this;
input.disabled = true;
}
})
$("#twobut").click(function () {
if (found === false) {
found = true;
threePick = true;
onePick=true;
var input = this;
input.disabled = true;
};
});
};
selector();
function enemy() {
$("#twobut").click(function () {
if (twoPick === true){
var input = this;
input.disabled = true;
startGame = true;
}
});
$("#threebut").click(function() {
if (threePick === true){
input.disabled = true;
startGame = true;
}
});
$("#twobut").click(function() {
if (twoPick === true){
var input = this;
input.disabled = true;
startGame = true;
}
});
}
enemy();
Notice that the enemy(); changes the startGame boolean to true
Now in the next part of my code:
function gameStart(){
if (startGame === true){
var button = $('<button/>', {
text: 'Attack'
})
$('#atkButton').append(button);
}
}
gameStart();
I want the function to run when the boolean is true but the boolean only changes locally. I know that accessing local variables is not possible but is there any way around this or an alternative I could use?
P.S I apologise for my inefficient code I've only been doing javascript for a few days.
Any help is appreciated!
gameStart() is called immediately after it is defined and only once. At this time the value of startGame is false.
enemy() sets up eventlisteners for clicks on buttons #threebut and #twobut. Only when one of these buttons is clicked is the value of startGame set to true. If gameStart() is called thereafter it will see that startGame is true.
Your two functions selector and enemy define event handlers for the click action on elements in your HTML. Unless you click on those elements these event handlers, and attached functions, are not called.
Your id=twobut element has three different event handlers attached which will all fire when that element is clicked.
Normally in jQuery you use $(document).ready(function() { ... }); in which you can call selector(); and enemy(); so this run when the DOM is properly loaded and the event handlers can be attached successfully.
Maybe add the HTML from your page to create a working example?

Cross-Listen to multiple events and trigger callback

I have a list of event names which I need to listen to stored in an array, like so:
var events = ['A', 'B'];
Now, I'm unsure which event will be triggered first and it could be very inconsistent (depends on the HTTP requests that they await) so I can never safely listen to only one of them. So, I need to somehow "cross-listen" to all of them in order to trigger my original callback.
So my idea was to do the following:
Create a listener for A, which creates a listener for B. B's listener triggers the callback.
Create a listener for B, which creates a listener for A. A's listener triggers the callback.
So this would result in 4 listners, which would look something like this (pseudo-code):
var callback = function() { console.log('The callback'); };
Event.on('A', function () {
Event.on('B', callback);
});
Event.on('B', function () {
Event.on('A', callback);
});
So I believe this would solve my issue (there's probably another problem that I'm not seeing here though).
The issue is, I can make this work when there are only 2 events I need to listen to. But what about when I have 3-4 events I want to "cross-listen" to? Lets say we have ['A', 'B', 'C', 'D']. This would obviously require looping through the events. This part is what's confusing me and I'm not sure how to proceed. This would need to register a nice combination of events.
This needs to be done in JavaScript.
My imagination and logic is limited in this case.
I was thinking something like this:
var callback = function() { console.log('The callback'); };
var events = {
'click': false,
'mouseover': false,
'mouseout': false
};
for(prop in events) {
$('.evt-button').on(prop, function(evt) {
if(events[evt.type] === false) {
console.log('First ' + evt.type + ' event');
events[evt.type] = true;
checkAll();
}
});
}
function checkAll() {
var anyFalse = false;
for(prop in events) {
if(events.hasOwnProperty(prop)) {
if(events[prop] === false) {
anyFalse = true;
break;
}
}
}
if(!anyFalse) {
callback();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button class="evt-button">The button</button>
There's lots of ways to do what you're asking, but to keep it simple you could have an array of event names, as you already do, and simply remove them as they occur, checking to see if the array is empty each time. Like this...
var events = ['A', 'B'];
var callback = function() { console.log('The callback'); };
var eventOccured = function(eventName) {
// if the event name is in the array, remove it...
var idx = events.indexOf(eventName);
if (idx !== -1) {
events.splice(events.indexOf(idx), 1);
}
// if the event array is empty then we've handled everything...
calback();
};
Event.on('A', function () {
// do whatever you need in the event handler
eventOccured("A");
});
Event.on('B', function () {
// do whatever you need in the event handler
eventOccured("B");
});
A bit late, but you can make a function that wraps your callback and reuse it to attach multiple eventlisteners and count until all have occurred. This way you can also add events to cancel out others, say keyup cancels keydown.
const K = a => _b => a
const makeEventTrigger = (fn) => {
let lastTarget,
required = 0,
active = 0;
return (enable, qualifier = K(true)) => {
required = enable ? required + 1 : required;
return (event) => {
if(!qualifier(event)) {
return
}
const isLastTarget =
lastTarget && lastTarget.isEqualNode(event.currentTarget);
if (!enable) {
lastTarget = isLastTarget ? null : lastTarget;
active = Math.max(0, active - 1);
return;
}
if (!isLastTarget) {
lastTarget = event.currentTarget;
active = Math.min(required, active + 1);
return active === required && fn();
}
};
};
};
let changeTitleCol = () =>
(document.querySelector("h1").style.color =
"#" + Math.random().toString(16).slice(-6));
let addTriggerForColorChange = makeEventTrigger(changeTitleCol);
let isSpace = e => e.key === " " || e.code === "Space"
document
.querySelector("button")
.addEventListener("mousedown", addTriggerForColorChange(true));
document
.querySelector("button")
.addEventListener("mouseup", addTriggerForColorChange(false));
document.addEventListener("keydown", addTriggerForColorChange(true, isSpace));
document.addEventListener("keyup", addTriggerForColorChange(false));
<h1>Multiple Event sources!</h1>
<div>
<button id="trigger-A">klik me and press space</button>
</div>

Send event when module was executed

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.

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();

Categories

Resources