REUSABLE UI COMPONENTS IN JAVASCRIPT
tybenz@adobe.com || @tybenz || github.com/tybenz || tybenz.com
#spamthenet || @spamthenet || spamthe.net || weekly@spamthe.net
• UI Patterns{~
• Decoupling HTML, CSS, and JS
• The JavaScript~}
{• UI Patterns}
• Decoupling HTML, CSS, and JS{~
• The JavaScript~}
{~• UI Patterns
• Decoupling HTML, CSS, and JS~}
• The JavaScript
• HTML => structure
{~• CSS => style
• JS => behavior/interaction~}
{• HTML => structure}
• CSS => style
{• JS => behavior/interaction}
{~• HTML => structure
• CSS => style~}
• JS => behavior/interaction
• Style only happens in CSS
{~• Animation only happens in CSS
• JS shouldn't care about markup~}
{• Style only happens in CSS}
• Animation only happens in CSS
{• JS shouldn't care about markup}
{~• Style only happens in CSS
• Animation only happens in CSS~}
• JS shouldn't care about markup
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">{~
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>~}
</ul>
{~<ul class="task-list">~}
<li class="task">{~
<input type="checkbox" name="groceries" />
Groceries~}
</li>{~
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>~}
{~<ul class="task-list">
<li class="task">~}
<input type="checkbox" name="groceries" />{~
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>~}
{~<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />~}
Groceries{~
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>~}
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
{~$(~} '.task input[type=checkbox]' {~).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});~}
{~$( '.task input[type=checkbox]' ).~}on{~(~} 'change'{~, function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});~}
{~$( '.task input[type=checkbox]' ).on( 'change', function () {
~}if ( this.checked ){~ {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});~}
{~$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
~}$( this ).closest( '.task' ){~.addClass( 'completed' );
}
});~}
{~$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).~}addClass( 'completed' ){~;
}
});~}
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
{~.task.complete {
display: none;
}~}
{~.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}~}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
{~.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}~}
{~.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}~}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
{~@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}~}
{~.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}~}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret{~ = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();~}
{~$.fn.secret = function () {
~}this.on{~(~} 'mousedown'{~, function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();~}
{~$.fn.secret = function () {
this.on( 'mousedown', function () {
~}$( this ).addClass( 'show' );{~
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();~}
{~$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});~}
this.on{~(~} 'mouseup'{~, function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();~}
{~$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
~}$( this ).removeClass( 'show' );{~
});
};
$( '.secret' ).secret();~}
{~$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};~}
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
{~1. Modular 1. Basic
2. User ignorance~}
1. Modular{~ 1. Basic
2. User ignorance~}
1. Modular 1. Basic{~
2. User ignorance~}
1. Modular 1. Basic
2. User ignorance
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
{~var Secret =~} Backbone.View.extend{~({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
~}el: '.secret'{~,
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',~}
events: {
'mousedown': 'apply',
'mouseup': 'remove'
}{~,
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
~}this.$el.addClass( 'show' );{~
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );~}
this.trigger( 'apply' );{~
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
~}this.$el.removeClass( 'show' ); {~
this.trigger( 'remove' );
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
~}this.trigger( 'remove' );{~
}
});
var secret = new Secret();~}
{~var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});~}
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
{~1. Event-driven 1. Backbone-dependent
2. Cleanly organized~}
1. Event-driven{~ 1. Backbone-dependent~}
{~2. Cleanly organized~}
1. Event-driven {~1. Backbone-dependent~}
2. Cleanly organized
1. Event-driven 1. Backbone-dependent
2. Cleanly organized
1. Event-driven
2. Cleanly organized
1. Event-driven
2. Cleanly organized
3. Modular
1. Event-driven
2. Cleanly organized
3. Modular
4. Flexible/customizable
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
{~var Lego = Class.extend({
defaultOptions: {},~}
init: function ( el, options ){~ {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});~}
{~var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {~}
this.$el = $( el );{~
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});~}
{~var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );~}
this.options = $.extend( {}, this.defaultOptions, options );{~
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});~}
{~var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},~}
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}{~
});~}
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {~}
this._super( el, options );{~
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );~}
this.$el.on{~(~} this.options.applyEvent{~, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({~}
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
}{~,
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {~}
self.apply();{~
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},~}
apply: function () { this.$el.addClass( this.options.applyClass ); }{~,
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();~}
self.trigger( 'secret-apply' );{~
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
{~var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});~}
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});{~
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});~}
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );{~
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});~}
{~var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );~}
secret1.on{~(~} 'secret-apply'{~, function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});~}
{~var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {~}
secret2.apply();{~
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});~}
{~var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});~}
secret1.on{~(~} 'secret-remove'{~, function () {
secret2.remove();
});~}
{~var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {~}
secret2.remove();{~
});~}
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
{~$.~}each{~( $(~} '.slice' {~), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});~}
{~$.each( $( '.slice' ), function ( index, slice ) {
var secret =~} new Secret{~( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});~}
{~$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {~}
applyEvent: 'mouseover'{~,~}
removeEvent: 'mouseout'{~
});
});~}
$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
github.com/tybenz/lego.js
tybenz@adobe.com || @tybenz || github.com/tybenz || tybenz.com