Calling events in modular JavaScript - javascript

I'm only learning how to work with the modules in JavaScript, so I have three separate .js files: main, listener and fileHandler
Simply this is a program that for every selected or dropped file(image) from computer gets appended to the page. Functions are working when I drag and drop files to the page, but when I select them through inputBox button files even files are stored in inputBox.files, they are not getting appended to the page.
var uploader = {};
uploader.init = function () {
this.inputBox = document.getElementById('uploadButton');
this.dropbox = document.getElementById('dropbox');
this.listener();
}
and listener method as:
probably I'm calling 'change' event wrongly here, that files are not appended.
uploader.listener = function () {
uploader.inputBox.addEventListener('change', uploader.fileHandler.addFiles(uploader.inputBox.files));
this.dropbox.addEventListener('drop', this.fileHandler.drop.bind(this));
}
one another constructor is:
uploader.fileHandler = new function () {
var uploadHandler = function () {...}
this.addFiles = function (files) {
Object.keys(files).forEach(function (key) {
var file = files[key];
uploadHandler(files[key]);
});
};
this.drop = function (event) {
event.stopPropagation();
event.preventDefault();
var files = event.dataTransfer.files;
this.fileHandler.addFiles(files);
};
}

EDIT
I see another issue. When you do this:
uploader.inputBox.addEventListener('change', uploader.fileHandler.addFiles(uploader.inputBox.files));
You are calling uploader.fileHandler.addFiles(uploader.inputBox.files) immediately and passing it's return value to .addEventListener(). Probably what you want instead is this:
uploader.inputBox.addEventListener('change', function() {
uploader.fileHandler.addFiles(uploader.inputBox.files)
});
Here you are passing an anonymous function reference which can be called later by the event handler.
This construct:
uploader.fileHandler = new function () {
this.addFiles = function (files) {
Object.keys(files).forEach(function (key) {
var file = files[key];
uploadHandler(files[key]);
});
};
}
only assigns a function to uploader.fileHandler. It does not define uploader.fileHandler.addFiles until you actually call that function (which you do not show).
I don't know why you're trying to nest your function definitions (that usually just causes more complexity than benefit in Javascript), but if you really wanted to define them that way, you could do this:
uploader.fileHandler = {
addFiles: function (files) {
Object.keys(files).forEach(function (key) {
var file = files[key];
uploadHandler(files[key]);
});
},
drop: function(...) {...}
};
This would then define both of these functions:
uploader.fileHandler.addFiles()
uploader.fileHandler.drop()

Related

Can I break an ES6 JavaScript program using the revealing pattern into separate files?

I have a large and growing JavaScript program that I'd like to break into separate files for maintainability but I don't know if it is possible. I have broken it into modules using the revealing pattern as described here. That helps and it may be all I can logically do.
This is a non-Rails Ruby/Sinatra/Rack middleware/ES6 JavaScript application. I have Sprockets implemented to maintain the asset pipeline. It uses jQuery Mobile Single Page Architecture which is required to maintain an active IoT WebSocket connection. As such, the HTML page and JavaScript function, once loaded, must always be maintained.
A mock-up of the JavaScript is:
$(function ($, window, document) {
let globalTriggered;
const constOne = [1, 2, 3];
const consDot = '.';
$("body").pagecontainer({
defaults: false
});
$(document).on("pagecreate", null, function () {
if (globalTriggered === false) {
globalTriggered = true;
let Module1 = (function () {
let privateMethod1 = function () {
Module2.anotherMethod2()
};
let someMethod1 = function () {
privateMethod1()
};
let anotherMethod1 = function () {
};
return {
someMethod1: someMethod1,
anotherMethod1: anotherMethod1
};
})();
let Module2 = (function () {
let privateMethod2 = function () {
};
let someMethod2 = function () {
Module1.someMethod1();
privateMethod2()
};
let anotherMethod2 = function () {
Module1.anotherMethod1()
};
return {
someMethod2: someMethod2,
anotherMethod2: anotherMethod2
};
})();
} // stabilzer end
}); // pagecreate end
}(window.jQuery, window, document)); // function end
What I would like to do would be to separate modules, like Module1 and Module2 in this example, into their own source files, again for maintainability.
I considered ES6's export/import option, but the import/export must always be done at the top level. Sprocket has a similar restriction in that it stops searching for directives once it hits code. I considered attempting to hack Sprocket past this by using require_self, but that probably won't work and it would be ugly if it did.
Any options? Thanks.
Well, it was nearly as simple as dandavis hinted (thanks). I am just moving the modules into their own file, executing the internal function when they are defined, eliminating their default execution [change their last line from })(); to }()); ], require them at the top using Sprockets (though ES6 export/import should work), and fire them when I need them.
//= require Module1.js
let Module1 = (function () {
let privateMethod1 = function () {
Module2.anotherMethod2()
};
let someMethod1 = function () {
privateMethod1()
};
let anotherMethod1 = function () {
};
return {
someMethod1: someMethod1,
anotherMethod1: anotherMethod1
};
}());
//= require Module2.js
let Module2 = (function () {
let privateMethod2 = function () {
};
let someMethod2 = function () {
Module1.someMethod1();
privateMethod2()
};
let anotherMethod2 = function () {
Module1.anotherMethod1()
};
return {
someMethod2: someMethod2,
anotherMethod2: anotherMethod2
};
}());
$(function ($, window, document) {
let globalTriggered;
const constOne = [1, 2, 3];
const consDot = '.';
$("body").pagecontainer({
defaults: false
});
$(document).on("pagecreate", null, function () {
if (globalTriggered === undefined) {
globalTriggered = true;
Module2.anotherMethod2();
} // stabilzer end
}()); // pagecreate end
}(window.jQuery, window, document)); // function end
Update: Any callbacks, including functions fired on something like click, must be exposed with the returned associative array and must use the module name in its reference even within that module since the callback will occur externally.

How to invoke a function from both an outside and a sibling function in javascript / google app script

Basic question but I can't figure it out :(. A solution to one makes the other one break. Here is the specific case narrowed down, any help is appreciated.
function onOpen() { // first entry point
var helper = new level1Function();
helper.level2FunctionA();
}
function onFormSubmit() { // second entry point
var helper = new level1Function();
helper.level2FunctionC();
}
function level1Function() {
this.level2FunctionA = function() {
console.log('hi');
}
function level2FunctionB() {
// how do I invoke level2FunctionA from here w/o breaking onOpen entry point?
}
this.level2FunctionC = function() {
level2FunctionB();
}
}
onOpen();
onFormSubmit();
// looking for 2 hi's to the console, one through each flow
create a reference to a variable self, assign to this at the top of the function body
function level1Function() {
var self = this;
this.level2FunctionA = function() {
console.log('hi');
}
function level2FunctionB() {
self.level2FunctionA();
}
this.level2FunctionC = function() {
level2FunctionB();
}
}
Another solution, instead of creating a reference to self as that is error-prone in many situations, you could use Function.prototype.bind and create a function boundLevel2FunctionB, which has this bound to the current level1Function instance (I see you're calling it using the new keyword).
Code:
[...] // level2Function body
function level2FunctionB() {
this.level2FunctionA();
}
var boundLevel2FunctionB = level2FunctionB.bind(this);
this.level2FunctionC = function() {
boundLevel2FunctionB();
}
[...]
Cheers!

Referencing another function in array of functions

I'm using require.js and have a library of functions I use in multiple places. I define the functions thusly:
define(function (require) {
"use strict";
var $ = require('jquery');
var UsefulFuncs = {};
UsefulFuncs.hideAlert = function() {
$('.alert').hide();
};
UsefulFuncs.loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
this.loadURL(url);
};
return UsefulFuncs;
});
Then, in my backbone view, I call a function from the library with:
UsefulFuncs = require('app/utils/useful_func'),
....
UsefulFuncs.linkClicked
This works fine for any standalone function in the library e.g. hideAlert(). However when one function in the library refers to another, such as linkClicked() calling loadURL(), I get an error:
Uncaught TypeError: Object [object Object] has no method 'loadURL'.
Any ideas how I can reference loadUrl()?
I would assume you set UsefulFuncs.linkClicked as a handler for an event.
If you were to use it like this:
UsefulFuncs.linkClicked()
, this inside the function would refer to UsefulFuncs, so this.loadURL() would be valid.
However, since you set it as a handler for an event, it can be called in other ways, and this is set to some other object (probably the element which triggered the event). To avoid the event handling mechanism changing your this, you can bind the function when you assign it:
element.onclick = UsefulFuncs.linkClicked.bind(UsefulFuncs);
Another way to go around it is to avoid using this in the handler, like so:
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
UsefulFuncs.loadURL(url);
};
This creates a closure over UsefulFuncs (the first method does as well, but it's more standardized).
Try something like this:
define(function (require) {
"use strict";
var $ = require('jquery');
var hideAlert = function() {
$('.alert').hide();
};
var loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
var linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
loadURL(url);
};
return {
hideAlert : hideAlert,
loadURL : loadURL,
linkClicked : linkClicked
};
});

How to mangage javascript files and encapsulate JavaScript/jQuery functions

I need to write & manage a lot of JavaScript code for current project.
I separate them into multiple .js files mainly based on module.
So, now i have for example:
Map.js // deal with google map issue
Common.js // common functions that will share by all modules
User.js // user module js code
Geofence.js // geofence module js code
etc.....
For example, inside my User.js file
what if i want to declare a function that only used inside the User.js file, not accessible by outside. what can i do?
var User = {};
User.registerModule = function () {
$('#user').click(function () {
Common.showLeftScrollbar();
getAllUsers();
// ...
});
}
function getAllUsers(){ // how to hide this function
// get
return users;
}
So, in my home page, i only need to coordinate with multiple .js files. Access what allows to access.
$(document).ready(function (data) {
GoogleMap.initialiseGoogleMap();
Common.refreshRightScrollbar();
User.registerModule();
// ...
});
It is my first time to write js and not enough time to study a whole book. So, please, in your opinion, is this structure ok with many js code? and how to hide functions that i dont want outside to access?
to hide that function you have different possibilities
just enclose your code in an immediate self-executed anonymous function
var User = {}; // this should not be enclosed too
(function() {
User.registerModule = function () {
$('#user').click(function () {
Common.showLeftScrollbar();
getAllUsers();
// ...
});
}
function getAllUsers(){ // how to hide this function
// get
return users;
}
})();
enclose that function inside User.registerModule function
User.registerModule = function () {
function getAllUsers() { ... }
$('#user').click(function () {
Common.showLeftScrollbar();
getAllUsers();
// ...
});
}
Place this function inside the scope:
User.registerModule = function () {
function getAllUsers(){ // how to hide this function
// get
return users;
}
$('#user').click(function () {
Common.showLeftScrollbar();
getAllUsers(); // returns users
// ...
});
}
And it will be private.
Now if you try to call this function outside it will be undefined:
getAllUsers(); // undefined.

Create a function like jQuery(document).ready

How can I do that?
It seems that you can have multiple jQuery's ready() functions, and they will all run when the DOM is loaded.
So how can I create my own ready()-like function? :)
function _addEvent(e, evt, handler){
if(evt == "ready")
evt = "DOMContentLoaded";
if(typeof handler !== 'function')return;
if (e.addEventListener)
e.addEventListener(evt, handler, false);
else if (e.attachEvent)
e.attachEvent("on" + evt, handler);
else
{
var oldHandler = e["on" + evt];
function newHandler(event){
handler.call(e, event);
if(typeof oldhandler === 'function')oldhandler.call(e, event);
}
}
}
var _events = ["ready", "click", "mousedown"]; //...
var _myLib = function(item){
function eventWorker(item, event){
this.add = function(handler){
_addEvent(item, event, handler);
};
}
for(var i=0;i<_events.length;i++)
this[_events[i]] = (new eventWorker(item, _events[i])).add;
};
var MyLib = function(item){
return new _myLib(item);
};
MyLib(document).ready(function(){alert("I am ready!");});
Test =>
http://jsfiddle.net/vgraN/
First, you need to identify what it is you need the function for - is it to respond to a particular browser event?
jQuery's $(document).ready(fn) uses an array internally to hold the functions to execute when the DOM has loaded. Adding a new ready(fn) call appends the function fn to the array. When the DOM has loaded (which is detected in various ways according to which browser the code is executing within), each function in turn in the array is executed. Any functions added using ready(fn) after the DOM has loaded are executed immediately.
In summary, you can use an array to store the functions to execute whenever it is that you need to execute them.
Take a look at domready, a standalone port of the ready(fn) function from jQuery to get some ideas about how to go about it.
It sounds like you want to make an array of functions and append new callbacks to it.
It's not easy to do cross browser.
If you assume the DOMContentLoaded event exists then you can just make
var domready = (function () {
var cbs = [];
document.addEventListener("DOMContentLoaded", function () {
cbs.forEach(function (f) {
f();
});
});
return function (cb) {
cbs.push(cb);
};
})();
You can use other fallbacks like window.onload and a hackish scroll check like jQuery does.
I'd recommend either using domready or reading the source.
Do you want to create a function which when passed a function will call that function at a particular time? (Also, it can be called multiple times.) If so this is how I would do it it. (Based on jQuery code.)
var funcQueue = (function () {
var funcList = [];
function runAll() {
var len = funcList.length,
index = 0;
for (; index < len; index++)
funcList[index].call(); // you can pass in a "this" parameter here.
}
function add(inFunc) {
funcList.push(inFunc);
}
})();
To use:
funcQueue.add(function () { alert("one"); });
funcQueue.add(function () { alert("two"); });
funcQueue.runAll(); // should alert twice.

Categories

Resources