Skip to content

Instantly share code, notes, and snippets.

@thm-design
Created July 16, 2013 07:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thm-design/6006647 to your computer and use it in GitHub Desktop.
Save thm-design/6006647 to your computer and use it in GitHub Desktop.
A CodePen by Nick Williams. JS module pattern demo - My current approach to the module pattern. A fully working widget that can be used multiple times on a page through a variety of means (raw object, jquery plugin, declaratively)
<!-- note this first widget doesn't get setup in JS -->
<!-- everything is done declaratively via our data-widget attribute -->
<div class="widget standard fl bordered" data-widget="dateTime">
<p class="widget-title">Declarative Widget</p>
<div class="widget-body">
<time data-time="date"></time>
<time data-time="time"></time>
</div>
</div>
<div class="plugin widget fancy fl bordered">
<p class="widget-title">jQuery Plugin Widget</p>
<div class="widget-body">
<time data-time="date"></time>
<time data-time="time"></time>
</div>
</div>
<div id="object-widget" class="widget fancy large fl bordered">
<p class="widget-title">Object Widget</p>
<div class="widget-body">
<a href="#" class="toggle-date">toggle date</a>
<time data-time="date"></time>
<time data-time="time"></time>
</div>
</div>
// so first, we want to create a completely new scope
// to work in, stops us polluting global namespace.
// we'll use a self-invoking function
// include semicolon to prevent issues if concatenating files
;(function($) {
/////////////////////////////////////////////////////////////
//// a contrived widget with a litle functionality
/////////////////////////////////////////////////////////////
// only constructors should begin with a capital letter!
// plus constructors are awesome because all dependencies
// get passed in, meaning we very easily unit test if need be :)
function DateTimeWidget(elem) {
this.$elem = $(elem);
this.$timeElem = this.$elem.find('[data-time="time"]');
this.$dateElem = this.$elem.find('[data-time="date"]');
this.timeTimer = null;
this.dateTimer = null;
this.hookupTime();
this.hookupDate();
};
// use a prototype because that way every instance of a DateTimeWidget
// can be extended at once, by anyone, to add new functionality
DateTimeWidget.prototype = {
constructor : DateTimeWidget,
// note use of $.proxy below!
// this ensures that the callback is always executed
// in the context of the object you specify - in this
// case the widget itself.
// all functions/fields are available without the need
// for another variable, 's', or whatever :)
hookupDate : function() {
this.updateDate();
this.dateTimer = setInterval($.proxy(this.updateDate, this), 60000);
},
hookupTime : function() {
this.updateTime();
this.timeTimer = setInterval($.proxy(this.updateTime, this), 100);
},
updateTime : function() {
this.$timeElem.text(new Date().toLocaleTimeString());
},
updateDate : function() {
this.$dateElem.text(new Date().toDateString());
},
hideDate : function() {
this.$dateElem.slideUp(100);
clearInterval(this.dateTimer);
},
showDate : function() {
this.$dateElem.slideDown(100);
this.hookupDate();
}
};
// also, latch onto something to expose to the wider world
window.DateTimeWidget = DateTimeWidget;
// while we're here, why not make it into
// a jQuery plugin?! why the hell not :)
// e.g. $(".my-widget").dateTimeWidget()
$.fn.dateTimeWidget = function(option) {
return this.each(function() {
var $this = $(this),
data = $this.data("dateTimeWidget");
// check if we'd already worked on this element
// if not, new up a widget and store against the element
if (!data) $this.data("dateTimeWidget", (data = new DateTimeWidget(this)));
// if a string was passed a string
// the intention is to call a method on the widget
// e.g. $(".widget").dateTimeWidget("hideDate");
if (typeof option === "string") data[option]();
});
};
// we can go one step further again by hooking in a data API!
// everything can then be set up purely declaratively from HTML
// e.g. <div data-widget="dateTime"></div>
// choose whatever naming convention you like
$("[data-widget=dateTime]").each(function() {
$(this).dateTimeWidget();
});
}(jQuery));
// this is our initialisation script
// where everything on the page gets hooked up
;(function($) {
var objectWidget = new DateTimeWidget(document.getElementById("object-widget")),
$pluginWidget = $(".plugin").dateTimeWidget();
// we can then interact with the widget differently
// depending whether working with jQuery plugin or directly with object object
$pluginWidget.dateTimeWidget("hideDate");
// so let's do something with our
// object widget as well, just for proof :)
// set up a click handler to toggle date
// note $.proxy again to save writing out an anonymous function
// NOTE: toggle() as used here has been removed from
// jQuery 1.9+
objectWidget.$elem.find(".toggle-date").toggle(
$.proxy(objectWidget.hideDate, objectWidget),
$.proxy(objectWidget.showDate, objectWidget)
);
}(jQuery));
body {
font-family: sans-serif;
}
.fl {
float: left;
}
.fr {
float: right;
}
.bordered {
border: 1px solid #333;
}
.large {
font-size: 150%;
}
.widget {
margin: 1em;
}
.widget-title {
font-size: 1.2em;
margin-bottom: 0.3em;
padding: 0.8em;
margin-top: 0;
}
.widget-body {
padding: 1em;
}
.widget time {
padding: 0.3em 0;
display: block;
}
.widget.standard {
box-shadow: 1px 1px 6px -2px #222;
}
.widget.standard .widget-title {
color: white;
text-shadow: 1px 1px 2px #000;
border-bottom: 1px solid #333;
background-color: red;
}
.widget.fancy {
/* oooh fancy, rounded corners lol */
border-radius: 0 0 0.4em 0.4em;
}
.fancy .widget-title {
/* ahh fancy gradients */
background: #7abcff; /* Old browsers */
background: -moz-linear-gradient(top, #7abcff 0%, #60abf8 44%, #4096ee 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#7abcff), color-stop(44%,#60abf8), color-stop(100%,#4096ee)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* IE10+ */
background: linear-gradient(to bottom, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7abcff', endColorstr='#4096ee',GradientType=0 ); /* IE6-9 */
}
.large .widget-body {
padding-top: 0;
}
.toggle-date {
font-size: 0.5em;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment