replace jquery's run only after DOM is loaded functionality - javascript

I am trying to remove dependence on jQuery. I have stolen a couple of functions I needed from underscore.js, but still need my code to run after the DOM is loaded in the same way as jQuery
Original
$(function(){
// all my code
})
Desired
var afterload = function(content){
// what goes here..?
}
afterload(function(){
// my code that should run after the DOM is loaded
})

There is a nice solution from https://github.com/freelancephp/DOMReady,
Here is a script
/**
* DOMReady
*
* #fileOverview
* Cross browser object to attach functions that will be called
* immediatly when the DOM is ready.
* Released under MIT license.
* #version 2.0.0
* #author Victor Villaverde Laan
* #link http://www.freelancephp.net/domready-javascript-object-cross-browser/
* #link https://github.com/freelancephp/DOMReady
*/
/**
* #namespace DOMReady
*/
var DOMReady = (function () {
// Private vars
var fns = [],
isReady = false,
errorHandler = null,
run = function ( fn, args ) {
try {
// call function
fn.apply( this, args || [] );
} catch( err ) {
// error occured while executing function
if ( errorHandler )
errorHandler.call( this, err );
}
},
ready = function () {
isReady = true;
// call all registered functions
for ( var x = 0; x < fns.length; x++ )
run( fns[x].fn, fns[x].args || [] );
// clear handlers
fns = [];
};
/**
* Set error handler
* #static
* #param {Function} fn
* #return {DOMReady} For chaining
*/
this.setOnError = function ( fn ) {
errorHandler = fn;
// return this for chaining
return this;
};
/**
* Add code or function to execute when the DOM is ready
* #static
* #param {Function} fn
* #param {Array} args Arguments will be passed on when calling function
* #return {DOMReady} For chaining
*/
this.add = function ( fn, args ) {
// call imediately when DOM is already ready
if ( isReady ) {
run( fn, args );
} else {
// add to the list
fns[fns.length] = {
fn: fn,
args: args
};
}
// return this for chaining
return this;
};
// for all browsers except IE
if ( window.addEventListener ) {
document.addEventListener( 'DOMContentLoaded', function(){ ready(); }, false );
} else {
// for IE
// code taken from http://ajaxian.com/archives/iecontentloaded-yet-another-domcontentloaded
(function(){
// check IE's proprietary DOM members
if ( ! document.uniqueID && document.expando ) return;
// you can create any tagName, even customTag like <document :ready />
var tempNode = document.createElement( 'document:ready' );
try {
// see if it throws errors until after ondocumentready
tempNode.doScroll( 'left' );
// call ready
ready();
} catch ( err ) {
setTimeout( arguments.callee, 0 );
}
})();
}
return this;
})();
and you can use like this
DOMReady.add(function (){
alert( 'DOM is ready!' );
});

I will leave my question here, because I asked after searching stack overflow for answers, but searched the wring terms I guess. I hope other people will find this answer useful.
Initially, I was intending a functionally equivalent to jQuery solution, but without the jQuery dependance. Turns out that is very large and complex...
https://stackoverflow.com/a/7053197/665261
I then decided I wanted a concise solution, because the reason for removing jQuery dependence was to make my code load faster on low spec devices.
I am only targeting Android mobile devices, I ended up using this:
function afterload(content){
/in/.test(document.readyState)
? setTimeout(function(){ afterload(content) }, 9)
: content()
}
It should work on desktop browsers too, apparently tested on all major ones. Details and a fix for FF<3.6 can be found here: http://dustindiaz.com/smallest-domready-ever

Related

How to pass an additional argument to the callback function in the Three.js loader.parse method

Parse method of Loader object in three.js allow you to set a callback function that is called when the parsing process is complete.
This callback is called passing it a unique argument that is the object parsed.
My problem is that i need an other argument to be passed to the callback.
This because i use parse method in a loop and i want create many callback eachone with a specific value of a variable.
If i set this value in the loop but outside the callback when the callback is executed unavoidably and obviously the value is always the last one setted in the loop.
This is the code:
for(var foldcont_index in foldcont) {
var foldit= foldcont[foldcont_index];
if(foldit.isDirectory()) { loadBFiles(fold+'/'+foldit.name); }
if(foldit.isFile()) {
var buigltf= fs.readFileSync(fold+'/'+foldit.name, 'utf8');
loader.parse(
buigltf,
undefined,
function(o) {
var oname= // !!! before issue with foldit.name
objectstank['xxx_'+oname]= o;
loadpoint= loadpoint+loadpercentage;
loadbar.set(loadpoint);
if(loadpoint>= 100) { document.getElementById("load-bar").style.display= 'none'; },
undefined
}
);
}
}
Can somebody help me to find a solution?
Read up on closures in Javascript
Unlike languages like C/C++ it's trivial to "close over variables" in JavaScript so you never need extra parameters for a callback in JavaScript because you can always "close over" whatever variables you need access to in the callback using a closure
In your case
for(var foldcont_index in foldcont) {
var foldit= foldcont[foldcont_index];
if(foldit.isDirectory()) { loadBFiles(fold+'/'+foldit.name); }
if(foldit.isFile()) {
var buigltf= fs.readFileSync(fold+'/'+foldit.name, 'utf8');
loader.parse(
buigltf,
undefined,
function(oname) {
return function(o) {
var oname= // !!! before issue with foldit.name
objectstank['xxx_'+oname]= o;
loadpoint= loadpoint+loadpercentage;
loadbar.set(loadpoint);
if(loadpoint>= 100) { document.getElementById("load- bar").style.display= 'none'; }
};
}(foldit.name),
undefined
);
}
}
might work. (I can read your code). This is no Loader object I know of in Three.js. There are lots of XXXLoader objects. No clue which one you're using.
This pattern
function(var1, var2, var3) {
return function() {
// do something with var1, var2, var2
};
}(value1, value2, value2);
Is a common pattern for closing over values. The code is a function that returns a function that has "closed" over var1, var2, and var3.
So you can pass the returned function to a callback. Long hand example
function makeCallback(var1, var2, var3) {
return function() {
console.log(var1, var2, var3);
};
}
const fn = makeCallback('Hello', 'World', '!');
setTimeout(fn, 1000);
The inline version
for (let i = 1; i <= 4; ++i) {
setTimeout(function(var1) {
return function() {
console.log(var1);
};
}(i), i * 500);
}
The following code might serve as a helpful example as it is designed to be as self-explanatory as I could make it. The callback function adds "initially invisible" models to the scene and also adds them to a list. Other methods in the class (not shown) take care of deciding dynamically when individual models should be made visible and where they should be placed in the scene.
import { GLTFLoader } from 'https://cdn.skypack.dev/three#0.133.1/examples/jsm/loaders/GLTFLoader.js'
export class transitVehicleSystem {
constructor(scene, dParamWithUnits, numWedges) {
this.scene = scene
this.unallocatedTransitVehicleModels = []
function prepareACallbackFunctionForLoader(myScene, myList) {
return function( {scene} ) {
const object = scene.children[0]
object.visible = false
for (let i=0; i<dParamWithUnits['numTransitVehicleModels'].value; i++) {
const tempModel = object.clone()
myScene.add(tempModel)
myList.push(tempModel)
}
}
}
const addTransitVehicles = prepareACallbackFunctionForLoader(this.scene, this.unallocatedTransitVehicleModels)
const loader = new GLTFLoader()
loader.load('models/TransitCar.glb',
// pass in the callback function that was created within a closure
addTransitVehicles,
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% transit car loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened', error );
}
)
console.log(this.unallocatedTransitVehicleModels)
}
*
*

Getting undefined when I expect otherwise

if you take a look at the following fiddle, you will see that I have created a class called: EventHandler which I then use as below (and in the fiddle):
/**
* Event Handler Class
*/
class EventHandler {
/**
* Core constructor.
*/
constructor() {
this.events = [];
this.beforeEvents = [];
this.afterEvents = [];
}
/**
* Register an event.
*
* #param {string} name
* #param {cb} callback
*/
register(name, cb) {
const event = {
name: name,
cb: cb
}
this.events.push(event);
}
/**
* Trigger an event based on name.
*
* #param {string} name
* #return {various}
*/
trigger(name) {
this.events.forEach((event) => {
if (event.name === name) {
return event.cb();
}
})
}
/**
* Get only the events.
*
* #return {array} events
*/
getEvents() {
return this.events;
}
}
const eventHandler = new EventHandler();
eventHandler.register('hello.world', () => {
return 'hello world';
});
alert(eventHandler.trigger('hello.world'));
When you run the fiddle, you get undefined, I expect to see 'hello world'.
Ideas?
In the trigger function you are returning undefined. Returning from inside a forEach callback does not return the parent function.
Try changing your code to something like this:
trigger(name) {
let msg = '';
this.events.forEach((event) => {
if (event.name === name) {
msg = event.cb();
}
});
return msg;
}
The problem is that you're alerting the response of eventHandler.trigger which doesn't actually return anything, hence the undefined. You could use something like Array.prototype.find to get find your event and then return the callbacks response like so:
trigger(name) {
let event = this.events.find(e => e.name === name);
if (event)
return e.cb();
return null;
}
A better method would be to store a key => value pair of event and callbacks, this way you won't have to find, you could just do something like:
trigger(event) {
return this.events[event] ? this.events[event]() : null;
}

JavaScript parameter value 'becomes' undefined when passed to constructor

I am creating a JavaScript component which I am creating instances of based on jQuery results however, the DOM element which I pass into the constructor, although populated when I step through the loop in the calling code, is undefined when passed to the constructor.
Here's my class and constructor...
export default class DeleteButton {
/**
* Creates an instance of DeleteButton.
*
* #param {object} element The DOM element to make into a delete button.
*
* #memberOf DeleteButton
*/
constructor(element) {
this.id = element.getAttribute("data-id");
if (!this.id) throw new Error("The 'data-id' attribute is required.");
this.deleteUri = element.getAttribute("data-delete-uri");
if (!this.deleteUri) throw new Error("The 'data-delete-uri' attribute is required.");
$(element).click(this.confirmRemove);
}
confirmRemove() { // does something }
}
and here's the calling code (This is a component manager that handles when to load components based on URLs / DOM state etc)...
export default class JsComponentManager {
constructor(onLoader) {
this._loader = onLoader;
this.select = {
deleteButtons: () => $(".js-delete-button")
}
this.result = 0;
}
bindComponents() {
const paths = new PathManager();
let $deleteButtons = this.select.deleteButtons()
if ($deleteButtons.length > 0) {
this._loader.add(this.renderDeleteButtons, $deleteButtons);
}
}
renderDeleteButtons($elements) {
$elements.each(() => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
}
This uses the following loader function to ensure that items are loaded...
/**
* Adds an event to the onload queue.
*
* #param {function} func The function to add to the queue.
* #param {any} param1 The first (optional) parameter to the function.
* #param {any} param2 The second (optional) parameter to the function.
*/
var AddLoadEvent = function (func, param1, param2) {
var oldonload = window.onload;
if (typeof window.onload !== "function") {
window.onload = () => { func(param1, param2); };
} else {
window.onload = () => {
if (oldonload) { oldonload(); }
func(param1, param2);
};
}
};
module.exports = {
add: AddLoadEvent
};
The onload management code seems to be running fine and, stepping through, code execustion is completely as expected until document.DeleteButtons.push(new DeleteButton(this)); - 'this' here is the DOM element, as I would expect, but as soon as the debugger steps into the controller the value is undefined.
Is this some odd scoping pain I've walked into?
renderDeleteButtons($elements) {
$elements.each(() => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
doesn't do what you think it does. jQuery relies on being able to set the this value of the callback function. But arrow functions don't have their own this, so jQuery cannot set the this value.
Inside the arrow function this will refer to whatever this refers to in renderDeleteButtons, which likely is an instance of JsComponentManager.
If you pass a function to another function and that function has to set the this value, you cannot use an arrow function. Use a function expression instead:
renderDeleteButtons($elements) {
$elements.each(function() {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
See also: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?
Maybe this helps to demonstrate the difference between an arrow function and a function declaration/expression:
// our library:
function each(values, callback) {
for (var i = 0; i < values.length; i++) {
// we use `.call` to explicitly set the value of `this` inside `callback`
callback.call(values[i]);
}
}
// Function declaration/expression
var obj = {
someMethod() {
"use strict";
each([1,2,3], function() {
console.log('function declaration:', this);
});
},
};
// Because we use a function expression, `each` is able to set the value of `this`
// so this will log the values 1, 2, 3
obj.someMethod();
// Arrow function
obj = {
someMethod() {
each([1,2,3], () => {
"use strict";
console.log('arrow function:', this);
});
},
};
// `this` is resolved lexically; whatever `each` sets is ignored
// this will log the value of `obj` (the value of `this` inside `someMethod`)
obj.someMethod();
I've now got this working by abandonning jQuery.each (which seems to have serious scoping issues passing the element from the array to anything else due to the way it messes with 'this'). I solved this by using a JS forEach call instead as follows. Discovering jQuery's makeArray method was the key. This is similar to what I had started with originally but was banging my head against forEach not working on a jQuery object...
renderDeleteButtons($elements) {
$.makeArray($elements).forEach((el) => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(el));
});
}
It also doesn't hurt my sensibilities by doing weird stuff with 'this' (for Felix)
See Felix's extra info on lexical scoping with 'this' at Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?

Stoping jquery .then chain via user input

This is probably a simple question, but i'm totally lost.
I have this function.
m.util.genericSwipeVertFunc = function (
ajaxRequest,
swipeOutTarget,
swipeInTarget
) {
var stage1, stage2, failStage, dfd = $.Deferred(), finalStage, functionPromise;
// Swipe of screen wait for ajax request
stage1 = function () {
return $.when(
ajaxRequest, // Returns $.Deferred()
m.util.animateDeffered(swipeOutTarget, "fadeOutDown", true) // Returns $.Deferred()
);
};
// Swipe and Show
stage2 = function () {
swipeInTarget.show();
return m.util.animateDeffered(swipeInTarget, "fadeInDown"); // Returns $.Deferred()
};
finalStage = function () {
dfd.resolve();
}
failStage = function () {
console.log("fail!");
swipeInTarget.hide();
};
functionPromise = stage1()
.then(stage2)
.then(finalStage);
$.when(functionPromise,dfd)
.fail(failStage);
return dfd;
};
Basically it does some fancy animations to fade in and out different response outputs from ajax functions. This all works fine, except when the user tries to change between targets very fast(before one chain finishes they start another) I get crazy animation all over the place.
I want to be able to reject the chain at any point by doing something like this.
// called on script load.
var currentAction = $.Deferred();
// Called everytime someone starts animation chain.
currentAction.reject();
currentAction = m.util.genericSwipeVertFunc(dfd, swipeOutTarget, swipeInTarget);
);
With my current code the failFunction is hit correctly but it doesn't stop the execution of stage2. So it hides then shows it and continues breaking things.
So to the question. How do I put a deferred in a chain that i can reject at any time during the chains execution ? :)
Example fiddle
http://jsfiddle.net/ff3jojbo/
Update for clarification
I am using animate.css for my animations. Not jquery animation.
I am more interested in how to stop the chain from starting the next stage at any point from user input.
Answer fiddle
http://jsfiddle.net/aefkwa8a/
Try using .queue() , .promise()
// array of functions to add to queue
var arr = [];
var swipeInTarget = $("#stage1");
var swipeOutTarget = $("#stage2");
// pseudo `ajax` call
var ajaxRequest = function ajaxRequest(next) {
return $.Deferred(function(d) {
setTimeout(function() {
d.resolve("ajaxRequest")
}, Math.random() * 5000)
}).promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function(data) {
console.log(data)
}).then(next)
}
var stage1 = function stage1(next) {
return swipeOutTarget.fadeTo(Math.random() * 5000, Math.random())
.promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function() {
console.log(this)
})
.then(next)
}
var stage2 = function stage2(next) {
return swipeInTarget
.show(Math.random() * 5000, function() {
return $(this).fadeTo(Math.random() * 2000, Math.random())
})
.promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function() {
console.log(this)
})
.then(next)
}
// do stuff when queue cleared
var failStage = function failStage() {
return swipeInTarget.hide(Math.random() * 2000)
.promise().then(function() {
console.log("m processes stopped")
})
}
// always do stuff when queue cleared,
// or all functions in queue complete
var finalStage = function finalStage() {
console.log("complete", this)
}
// create jQuery object
var m = $({
m: arr
});
// add function to `"stages"` queue
m.queue("stages", [stage1, stage2, finalStage]);
// do stuff when all functions complete , or queue cleared
m.promise("stages")
.then(finalStage);
// dequque `"stages"` queue
m.dequeue("stages");
// clear `"stages"` queue
$("button").click(function() {
m.queue("stages", [])
.promise("stages").always(failStage)
})
#stage2 {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>stop m processes</button>
<div id="stage1">stage1</div>
<div id="stage2">stage2</div>
OP's own solution here can fail after several clicks. In particular, if button is clicked while a section is flying in, then the latest demanded section may fly in, then disappear.
This solution is completely different.
Instead of using jQuery's queue/dequeue, it uses a regular stage1().then(stage2) promise chain, and stops progress down that chain by removing the CSS animation classes from the animated element and detaching its animationend handler, thus ensuring the promise associated with completion never resolves.
As you will see, much of the functionality is factored as jQuery plugins, which makes for convenient, compact syntax.
$(function () {
// **************************
// *** Various outer vars ***
// **************************
var $sections = $('#TabSection>div').hide();
var ajaxPromise;
var str = {
//various strings
'animationend': 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
'fadeOutClasses': 'fadeOutDown animated',
'fadeInClasses': 'fadeInDown animated',
'allClasses': 'fadeOutDown fadeInDown animated'
};
// ***********************************************
// *** Utilities in the form of jQuery plugins ***
// ***********************************************
jQuery.fn.killAnim = function(animation) {
/* jQuery plugin :
* Remove all the animation classes from all possible targets, and
* detach any currently attached animationend handlers.
* Depends on: str (object).
*/
return this.off(str.animationend).removeClass(str.allClasses);
};
jQuery.fn.cssAnimate = function (animation) {
/* jQuery plugin :
* Perform CSS animation and return promise.
* Depends on: str (object); killAnim (plugin).
*/
var that = this;
return $.Deferred(function(dfd) {
// if no target or target not visible, resolve;
if(that.length == 0 || !that.is(':visible')) {
dfd.resolve();
}
that.addClass(animation).one(str.animationend, dfd.resolve);
}).then(function() {
that.killAnim();
});
};
jQuery.fn.genericSwipeVertFunc = function () {
/* jQuery plugin :
* Sequence two CSS animations - fadeOut then fadeIn.
* Depends on: str (object); killAnim (plugin); cssAnimate (plugin).
*/
var that = this; // swipeInTarget
var swipeOutTarget = $sections.filter(':visible()').eq(0);
function stage1() {
$sections.killAnim().not(swipeOutTarget).hide();
return swipeOutTarget.cssAnimate(str.fadeOutClasses).then(function() {
swipeOutTarget.hide();
});
};
function stage2() {
$sections.not(that).killAnim().hide();
return that.show().cssAnimate(str.fadeInClasses);
};
return stage1().then(stage2);
};
// **********************
// *** Event handlers ***
// **********************
$('button').on('click', function (event) {
var inTarget = $($(this).data('tar'));
if(ajaxPromise) {
ajaxPromise.abort('aborted');
}
// *** start: emulate AJAX ***
ajaxPromise = $.Deferred(function(dfrd) {
setTimeout(dfrd.resolve, 1000);
});
ajaxPromise.abort = ajaxPromise.reject;
// *** end: emulate AJAX ***
ajaxPromise.then(function() {
return inTarget.genericSwipeVertFunc();
}).fail(function(e) {
$sections.killAnim().hide();
console.log(e);
});
});
});
I believe this solution to be more reliable. Even with lots of manic clicking, I could not defeat it.
Try it here

JavaScript organization | Module pattern w/ modules

I'm organizing my code into 20-60 line modules, usually in the module pattern. I want a well formed object oriented JavaScript library.
Is this the best way to do this? The code has been tested and works.
I like it because a programmer can pull modules from the library and use them as needed, they are self contained.
Here is Tool, Message, Effect and Text, all contained in NS.
Question?
Is this a good way ( best practice ) to organize my library?
Note
So far, there is 0 consensus in the comments and answers...very frustrating.
Outer Module Pattern
var NS = ( function ( window, undefined )
{
/* All Modules below here */
} )( window );
Tools
/**
*Tools
* getTimeLapse - benchmark for adding
*/
var Tool = ( function ()
{
var Tool = function ( )
{
};
Tool.prototype.getTimeLapse = function( numberOfAdds )
{
var end_time;
var start_time = new Date().getTime();
var index = 0;
while ( index <= numberOfAdds )
{
index++;
}
end_time = new Date().getTime();
return ( end_time - start_time );
};
return Tool;
} () );
Message
/**
*Message
* element - holds the element to send the message to via .innerHTML
* type - determines the message to send
*/
var Message = ( function ()
{
var messages =
{
name: 'Please enter a valid name',
email: 'Please enter a valid email',
email_s: 'Please enter a valid email.',
pass: 'Please enter passoword, 6-40 characters',
url: 'Please enter a valid url',
title: 'Please enter a valid title',
tweet: 'Please enter a valid tweet',
empty: 'Please complete all fields',
same: 'Please make emails equal',
taken: 'Sorry, that email is taken',
validate: 'Please contact <a class="d" href="mailto:foo#foo.com">support</a> to reset your password',
};
var Message = function (element)
{
this.element = element;
};
Message.prototype.display = function( type )
{
this.element.innerHTML = messages[ type ];
};
return Message;
} () );
Effects
/**
*Effects
* element - holds the element to fade
* direction - determines which way to fade the element
* max_time - length of the fade
*/
var Effects = ( function ()
{
var Effects = function ( element )
{
this.element = element;
};
Effects.prototype.fade = function( direction, max_time )
{
var element = this.element;
element.elapsed = 0;
clearTimeout( element.timeout_id );
function next()
{
element.elapsed += 10;
if ( direction === 'up' )
{
element.style.opacity = element.elapsed / max_time;
}
else if ( direction === 'down' )
{
element.style.opacity = ( max_time - element.elapsed ) / max_time;
}
if ( element.elapsed <= max_time )
{
element.timeout_id = setTimeout( next, 10 );
}
}
next();
};
return Effects;
} () );
Text
/**
*Text
* form_elment - holds text to check
*/
var Text = ( function ()
{
var Text = function ( form_element )
{
this.text_array = form_element.elements;
};
Text.prototype.patterns =
{
prefix_url: /^(http:)|(https:)\/\//,
aml: /<(.+)_([a-z]){1}>$/,
url: /^.{1,2048}$/,
tweet: /^.{1,40}$/,
title: /^.{1,32}$/,
name: /^.{1,64}$/,
email: /^.{1,64}#.{1,255}$/,
pass: /^.{6,20}$/
};
Text.prototype.checkPattern = function( type )
{
return this.patterns[ type ].exec( this.text_array[type].value );
};
Text.prototype.checkUrl = function( type )
{
return this.patterns[ type ].exec( this.text_array.url.value );
};
Text.prototype.checkSameEmail = function()
{
return ( ( this.text_array.email.value ) === ( this.text_array.email1.value ) );
};
Text.prototype.checkEmpty = function()
{
for ( var index = 0; index < this.text_array.length; ++index )
{
if ( this.text_array[ index ].value === '')
{
return 0;
}
}
return 1;
};
return Text;
} () );
The one thing I would suggest to change to make your code cleaner and reduce its footprint is to just set the prototype property at once, so that instead of doing
Object.prototype.method1 = function(){};
Object.prototype.method2 = function(){};
You do
Object.prototype = {
method1: function(){},
method2: function(){}
};
If you need to conserve the constructor reference, which is recommened, you should re-assign the constructor afterward. See this answer for more details.
I personally prefer using a modular code organization library like ncore
This encourages you to write your code as a set of modules (one module per file) and then hook them together using dependency injection and bootstrapping.
The code is slightly portable because modules are just objects on their own right, however if one doesn't use ncore advantages are lost.
The leaderboard app shows a detailed example of OO code organization
A few suggestions... First would be to create a namespace object as scope for your libraries... jQuery uses "jQuery" and "$", underscore uses "_". I tend to use "CompanyName.SiteName"
if (typeof CompanyName == "undefined") var CompanyName = {};
CompanyName.SiteName = CompanyName.SiteName || {};
The first line explicitly checks against undefined, as you'll get errors in many browsers otherwise for root variables using the method on the SiteName property.
From there, I would make a couple adjustments... When you are calling an anonymous function inline, it's best to wrap the whole of the call inside the parens.
CompanyName.SiteName.ModuleName = (function(w){
...
return moduleImplementation;
}(window || this)); //CompanyName.SiteName.ModuleName
This tends to avoid the confusion by having the parens wrap the whole, and by having a comment at the end of the module declaration.
Per the comment above, you may want to make the prototype declaration as a more singular statement. I would advise against this, as longer modules can make readability an issue.
myModule.prototype = {
"method1": function(){
}
...
"methodN": function(){
//by the time you get here, you may not see the top, and where you are nested in
}
};
//with the dot-notation, or hash notation
myModule.prototype.methodN = ...
myModule.prototype["methodN"] = ...
//you can see where you are binding to at that function
You may also want to look into RequireJS and AMD
There's also the concept of dealing with simpler objects, and using functional binders. Treating your library as a set of functions (similar to C exports) that are passed and work with simpler objects/types. It really depends on your needs/usage and the specifics of your needs and use.
You may also want to look at javascript libraries like KnockoutJS, Underscore and Backbone for some examples.

Categories

Resources