h5bp/Effeckt.css

Create an Effeckt.js module/core

toddmotto opened this issue · 18 comments

From a code reuse and modular perspective, I'd like to see some JavaScript modules (Patterns/AMD/UMD/whatever) over the singleton Objects - that allow for easy name-spacing and extensibility - plus out of the box functionality for developers for those who aren't great with JS.

If Effeckt.js comprised of something like this (on a basic level, I prefer a UMD setup):

(function (Effeckt) {

    // do something with Effeckt

})(this.Effeckt = this.Effeckt || {});

Of which can be extended with different modules or act on it's own, developers can include modules they like without having to call differently named Objects, and it offers an Object to bolt new functionality into.

Well, right one time, I'm doing some JavaScript rewrite.

Effeckt.js would be a object containing the same as it has but other modules as well.

var Effeckt = Effeckt || {};

(function() {
  Effeckt.modal = function(something) {
    // Something
  }
})();

something like that is how is to going to be, at least what I'm writing.

P.S. it what you said just letting you all know.

@wellingguzman What we need to know is what the benefits are from what you have compared to what @toddmotto is suggesting. From what I can decipher from Welling's code it would be something like this…

(function() {
  Effeckt.modal = function(something) {
    // Something
  },
  Effeckt.pageTransition = function(something) {
    // Something
  }
})();

@toddmotto Can you also expand a bit more with your example?

Why not as below to stop having to type Effeckt everytime

var Effeckt = {
    modal: function() {
        // Something
    },
    pageTransition: function(something) {
        // Something
    }
};

ok, what I suggest is what he said, but just instead of passing the Effeckt object we use it as global object.

@AaronLayton because if the author want modal but not pageTransition, we need to add keep all modular and extendable.

so here is the deal;
Let's say we want modal only. We have to include js/Effeckt.js and js/modules/Modal.js;

we are going to have all global things like what's the name of the animationEnd event (according to Modernizr) on Effeckt.js, something like this.

// Effeckt.js
(function(root){
  var Effeckt = function()  {
    // Something global
  }

  root.Effeckt = Effeckt;

})(window);

if we want modal we extend Effeckt object adding Modal object.

// Modal.js
var Effeckt = Effeckt || {};

(function(root){
  var Modal = function()  {
    // Something here
  }

  Modal.methods = here;
  Modal.prototype.methods = hereToo;

  root.Effeckt.modal = Modal;
})(window);

this way it only includeEffeckt.js and Modal.js.

if we want to use other module we extend it the same way.

+1 for IIFE pattern for better javascript maintainability and flexibility.

P.S. The readme/tutorial should to tell the user to include the global.js first then include selected individual module, "only after the global". Otherwise the module cannot read global and would encounter error/bugs.


Suggestion:
+Make module more declarative+informative with metadata
+Dependency Checking for Reminding the user of missing global file.

For example:
Module of modal effect

//Modal.js
;(function(root){
  //metadata
  var dependsOnGlobal  = true;   
  var namespace = 'Modal';


  //cache/shorcut for accessing global vars/utility
  var global = root.Effeckt; ;
  //module itself :)
  var EffecktModule = {
     property1: 'abc',
     property2: 'def', 
     method1: function(){
        //logic that access global vars 
        if (global.isTouch){
         //do blah blah
        }
        //accessing global util
        var prefixedEventRef = global.prefixAnimEvents('animation');

        return magic;
     },
     method2: function(){}
  }; 

  //Remind user to include global.
  if (dependsOnGlobal && typeof root.Effeckt === 'undefined'){
   throw Error('Missing Global Dependency');
   return;
  }

  //exporting.
  root.Effeckt = root.Effeckt || {};
  root.Effeckt[namespace] = EffecktModule;
})(window);

The checking mechanism of global dependency helps the module more robust

Thanks guys for the input - really great ideas. From my perspective @AaronLayton - dropping the single Object Literal declaration and wrapping things inside an IIFE allows for us to keep private methods instead of allowing all the properties to be visible publicly. Better encapsulation.

Here's an idea using a UMD approach which allows Async module, Browserify/Node and browser globals to be used:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof exports === 'object') {
        module.exports = factory;
    } else {
        root.Effeckt = factory();
    }
})(this, function () {

    'use strict';

    var exports = {};

    var privateMethod = function () {

    };

    exports.publicMethod = function () {

    };

    return exports;

});

As it returns an Object, we can easily extend it. We don't need to create a new UMD wrapper, but can extend the initial one:

(function (Effeckt) {

    'use strict';

    var exports = Effeckt || {};

    var privateMethod = function () {

    };

    exports.publicMethod = function () {

    };

    return exports;

})(this.Effeckt);

We could then add UMD support to this too.

This pattern (unless it can be refined furthermore) saves some repetition and checking namespaces (such as the quick example I first shown, prevents passing in the full window comparison and instead we just pass the Object reference around and extend as needed).

Interestingly, the above extension example would allow it to work without the core also, should the module not need the core... BUT... should it need the core (which I assume most will):

(function (Effeckt) {

    'use strict';

    if (!Effeckt) return; // return or throw new Error()

    var exports = Effeckt; // only export to the core

    var privateMethod = function () {

    };

    exports.publicMethod = function () {

    };

    return exports;

})(this.Effeckt);

Well aren't we going to far complicating it?

I know that this approach has it benefits from what I suggest, but we got to keep it as simple as we can.

Node.js isn't on our map right now.

It just a thought.

I personally don't think it'd be overcomplicating it. Some developers I expect will have used an AMD approach and can make use of the anonymous AMD definition. And those using Browserify (which is increasing in popularity) will take the module.exports declaration - so it's a win for everybody :) appreciate that Node isn't - but Browserify is a great use case which uses the same syntax.

I'd also keep in mind that this project will eventually become little packages of joy for authors possibly using bower plus downloaded from the site itself and on npm.

I know, I don't take that off "the plan", I just saying we need to delivery something that works then do what you are suggesting, because as now, JavaScript code doesn't provide or aren't used, JavaScript is there to help CSS.

What I'm saying is that we don't do something like Effeckt.openModal('#aModal'); a method to be used outside the project itself, so why bother?

This is what I believe is the best thing we can do, focusing on CSS and JavaScript as complementary and as simple as possible.

So, what's up with this?

I like the sentiments in the comments from @wonglok. If this is gonna have some global.js dependency then best to tell an author in some way. We want authors to quickly be able to use these modules in a fashion like:

<link href="effeckt-modal-[type].css" rel="stylesheet">
<script src="effeckt-modal-[type].js"></script>

Whatever makes that happen for an author gets my vote. We don't want to encapsulate all the effects in one block. I think the guts of the functionality (global.js for example) need to live as one object and then the diff types of effects (modal-fadein.js) can be extensions of that object.

Like your comments @grayghostvisuals. Ideally (should there be enough shared/common JS) the guts should live in the "core" - and only lightweight extensions hang off it in new files/extensions reaping all the core goodness too. Do you think there's a need for a core, or just a namespace?

Here's a few scenarios:

Scenario 1: Core Object and Module extension

<script src="effeckt.js"></script> <!-- 10kb -->
<script src="effeckt-[module].js"></script> <!-- 0.8kb -->

Scenario 2: No Core Object and standalone Module

<script src="effeckt-[module].js"></script>

Scenario 1 is very similar to how most libraries work, such as jQuery UI relying on jQuery, Angular Routes/Scenarios relying on Angular - both being initial includes.

@grayghostvisuals
+1 for submodule approach for easier contributing and debugging, and quickly use modules.

There are several things to consider:

  1. Packaging of code, Global-> modal module-> sub-module for each author.
  2. Use it quickly and easily in 'dead-simple-vanilla' examples
  3. Yeoman gunrt-usemin driven, like there are so many boostrap js modules in yeoman.
<!-- build:js scripts/plugins.js -->
<script src="bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/affix.js"></script>
<script src="bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/alert.js"></script>
<script src="bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/dropdown.js"></script>

<!-- endbuild -->

4 Ease of use of pre-packaged single JS file. (Even Though this is not encouraged in production for the sake of file size. you know, some css driven staggering effeckt has large css file size)

5 Ease of packaging with web-customizable downloader. ***

The web-builder-downloader could be like this: ([x] is a checkbox)
[x] global (mandatory for depended modules)
[x] module-offscreennav-reveal-then-minimize
[x] module-offscreennav-left-push
[] module-offscreennav-left-reveal
[] module-modal-[type]
[] module-modal-[type]
[] module-modal-[type]
...
...

With IIEF extension / Bootstrap's JS module approach it would be very customisable for the user to choose which lib they need with both yeoman + web-customizable-downloader


@toddmotto
Scenario 1 seemed better.

Does 2 always have to include core if it requires shared code?
It might create redundant "core" code in every module.

I found this web-js-lib-builder-3js might inspire our module/core approach/direction.
http://marcinwieprzkowicz.github.io/three.js-builder/

I vote up on this scenario.

Scenario 1: Core Object and Module extension

<script src="effeckt.js"></script> <!-- 10kb -->
<script src="effeckt-[module].js"></script> <!-- 0.8kb -->

same as jQuery UI

Vote up +1~ for core/module approach