Custom Events in JavaScript

Posted by stephen on December 29th, 2009

Usually I use jQuery for custom events, because it has a very nice model. However, recently I have been working on a JavaScript class that I want to be independant of other libraries, so I needed to create my own custom event model. Note: This class does make use of John Resig’s Array.remove() method.

For the sake of this sample, I changed the namespace to “ns”. You can change that to whatever you’d like.

Eventify

To enable an object with events, you’ll use the ns.events.eventify(obj) method. Once that is done, your object will have “events” and “__listeners” properties, in addition to “dispatchEvent(eventType, eventData)”, “addEventListener(eventType, callback, bubbles)”, and “removeEventListener(eventType, callback||listener)” properties.

You may pass one or many objects to the eventify() method.
ns.events.eventify(obj);
or
ns.events.eventify(obj1, obj2, obj3);

You do not need to interact with the “events” and “__listeners” properties at all, unless you want to predefine a set of events for a particular object, usually for documentation purposes. Here’s an example of a custom class that uses this event model and predefines its events. Just to clarify, I am using the “id” property in this class primarily for the event bubbling example that follows. It is not needed at all for the events to work.

addEventListener() / dispatchEvent()


ns.MyClass = ({
function MyClass(id){
ns.events.eventify(MyClass.prototype);
this.id=id;
}
MyClass.prototype={
id:null,
events:{
ready:new ns.events.Event("ready"),
error:new ns.events.Event("error")
},
property:null,
method:function(){}
}
return MyClass;
})();

If you do not define the events explicitly in the events property, they will be added automatically when a listener is added to the object. For instance:

var my = new ns.MyClass("my");
my.addEventListener("ready", function(){
alert("my is ready");
});

Would define my.events.ready.

dispatchEvent() and passing data

To dispatch an event, use the dispatchEvent() method. To pass data with a dispatched event, use the eventData parameter. For example:

my.addEventListener("ready", function(evt, data){
alert(data.hello);
});
my.dispatchEvent("ready", {hello:"Hello from dispatcher."});

Event bubbling

Event bubbling is somewhat manual. Because these are custom objects with no inherent heirarchy, you must define the parent object as the event’s “bubbleTo” property. The object defined as “bubbleTo” must share the event type that is being dispatched on the current object (they do not need to be instances of the same class). Impliment bubbling as follows:

var parent = new ns.MyClass("parent");
var child = new ns.MyClass("child");
child.events.ready.bubbleTo = parent;

child.addEventListener("ready", function(evt){
alert("sincerely, "+evt.target.id+".");
});
parent.addEventListener("ready", function(evt){
alert("Sincerely, "+evt.target.id+". With regards, "+evt.currentTarget.id+".");
});

child.dispatchEvent("ready");

To stop the bubbling or chain of event callbacks, call evt.stopPropagation() from within a listener function. This will prevent subsequent and parent handlers from being called.

You may also prevent the bubbling of an event when creating the listener by using the “bubbles” parameter of the “addEventListener()” method.

var listener = child.addEventListener("ready", function(evt){
alert("sincerely, "+evt.target.id+".");
}, false);

removeEventListener()

Finally, to remove a listener from an object, use the removeEventListener(listener||callback) method. This method will accept the listener or the callback function used. Note that in the above example I have assigned the listener to “var listener”.
child.removeEventListener("ready", listener);
You may also use the remove() method of the listener itself.
listener.remove();

Download

Right click and choose “Save Target As” to download events.js.

Javascript Date difference as milliseconds, minutes, hours, days, weeks, months, or years

Posted by stephen on November 13th, 2009

I needed to figure out the difference between two dates in Javascript, so I wrote up this function. It takes into account the days in each month as well as leap years. It will calculate years, months, weeks, days, hours, minutes, seconds, and milliseconds, or any combination of those. For example, if you want months and hours, this’ll do ya.

Let me know if anyone finds a discrepancy in the calculations.

sample.html
timeSpan.js

View source of the sample page above for usage. Samples include a date comparison and a date countown.

IE6 and form.submit() bug workaround

Posted by stephen on November 3rd, 2009

IE6………one of life’s great mysteries. I think we may need some federally enforced browser standards more than a new health care system, but that’s just me.

In IE6, programatically triggering the submit() method of a form element doesn’t *always* seem to work. There’s no logical reason - it just doesn’t. I’ve run into this problem twice now. The first time, I just ended up building a query string out of the form variables and using window.location to send them to the action page. That was with a static form. I just hit this wall again with a form I’m building dynamically.

For *whatever* reason, delaying the submit action by 100ms works like a charm. I wish I could tell you more, but as with all IE6 bug fixes, when they work, I try not to ask questions.

Here’s what I ended up doing:

I tried building the form through the browser’s native methods, using document.createElement() and element.appendChild(), which didn’t make a difference, so I reverted back to created the form with jQuery. So, here’s what I ended up doing….

/* ....build form elements using jQuery... */
$j("body").append(form);

setTimeout(function(){
	form.submit();
}, 100);

Hope this saves someone some time! If you’re dealing with a form that is not being built dynamically, I have an inkling that this may not work. Still, delaying the event another way might. Try this, and if it works for you, let me know!

<a href="javascript:;" onclick="setTimeout(function(){ form.submit() }, 100)">Submit</a>

That’s the onclick event of an anchor tag, but the same would apply for the onclick of any element.

Apache 2.2.14, PHP 5.2.11 on Windows 2003

Posted by stephen on October 20th, 2009

I just spent (feels like wasted) 6 hours trying to do a simple Apache/PHP install on a Win2K3 server.

I installed Apache without a hitch. Got the lovely “It works!” page, and then moved on to the PHP install. After trying the auto-install and binary packages, Apache simply would not start and would throw the following error in the Windows Application log:

Faulting application httpd.exe, version 2.2.14.0, faulting module php5ts.dll, version 5.2.11.11, fault address 0×000f330d.

I tried to Start > Run > “cmd” > “C:\php\php.exe -v” [enter] and kept getting DLL errors like “This application has failed to start because xyz.dll was not found. Re-installing the application may fix this problem.” I started tracking down the DLLs, but every one I fixed would expose another missing one.

Finally, I stumbled onto this PHP bug report where bwacker listed some of the same DLLs I was having trouble with. I commented out the lines referencing those DLLs in the php.ini file. The errors disappeared, and Apache started right up!

Here is bwacker’s list. Not all of the references were in my php.ini, but removing the ones that were there did the trick.

;extension=php_ifx.dll 		needs ISQLT09A.DLL
;extension=php_iisfunc.dll	not in /extensions
;extension=php_mcrypt.dll	needs LIBMCRYPT.DLL
;extension=php_oci8.dll		needs OCI.DLL
;extension=php_oracle.dll	needs OCIW32.DLL
;extension=php_printer.dll	not in /extensions
;extension=php_sybase_ct.dll needs LIBCT.DLL and LIBCS.DLL

Obviously, if your application requires any of those DLLs, you will need to resolve the absences, but most of you will not need them.

Hope this saves you some time!

jquery.tabPane()

Posted by stephen on February 26th, 2009

Everyone enjoys a good set of tabbed panes. I wrote jquery.tabPane to give us a simple way to do that, because this particular site is using Adobe Contribute as the CMS. Basically, you create some links to anchors, then tell this function what the containing elements for those are.

Usage

jQuery.tabPane(tabPaneSettings, callback);

Example

This isn’t so much of an example as a clarified description. If you want to see a sample, go here. It also has some HTML and CSS selectors you can use as a base, if you’d like.

Let’s say you have a list of anchor links, and several divs with content. You can point the tabPane settings at the containers of the links to those anchors (tabs), and the containers of those anchors (pane). The display/hide functionaly is handled for you, and you can style it any way you want.

Here you have it:

var paneSettings = {
	tabContainers:"div.pane-buttons li",
	contentContainers:"div.pane-content div.content-pane"
};
j.tabPane(paneSettings);

Javascript CSS Formatter

Posted by stephen on February 17th, 2009

I’ll probably post more on this later, but I just want to get it up for now.

Sometimes my CSS gets auto-formatted, which adds a whole bunch of “clutter” space that I don’t want. I find CSS easier to read when there is a visual hierarchy of the selectors and the HTML elements they represent, and if there is consistency in the order of the selector attributes.

The following CSS Formatter will place all of your selectors and their contents on a single line, and sort their attributes alphabetically.

For instance…

div#whatever, div.whatnot {
display:block;
background:transparent;
float:left;
}

becomes…

div#whatever,
div.whatnot {background:transparent; display:block; float:left;}

The formatter will IGNORE any space outside of the selector and its contents.

Try it out here:

http://blog.stephenrushing.com/wp-content/uploads/2009/02/cssFormat/cssformat.html

Getting an element’s html, not just the innerHTML

Posted by stephen on January 13th, 2009

The original content of this post involved a little function I wrote called “getNodeHtml”, which basically wrapped a given HTML element with a parent node, got the innerHTML of that temporary parent node, reset the DOM to its previous state, then returned the HTML string. I also used jQuery in the function, which seemed unnecessary to me.

Today I saw that IE has an outerHTML property, which does exactly what I need. I generally avoid giving IE credit, but they get a chocolaty kudos bar for this one.

The Array.indexOf() function I have gave me the idea to extend the prototype of the HTMLElement object so I could have it at my fingertips, and use the same property call in FF and IE (amoung others). After spending some time trying to add a “get outerHTML” property to the HTMLElement object, I found some useful info on Javascript Getters and Setters that pointed me in the right direction. The result…
if(document.__defineGetter__ && !HTMLElement.outerHTML){
HTMLElement.prototype.__defineGetter__(”outerHTML”, function(){
var parent = this.parentNode;
var span = document.createElement(”span”);
span.appendChild(this);
var HTML = span.innerHTML;
parent.appendChild(this);
delete span;
return HTML;
});
}

The __define[SG]etter__ methods do not exist in IE, so I’m still curious how to add a getter/setter to an existing prototype there. We could just create functions like value(’test’, ’set’) / value(null, ‘get’) or setValue(’test’) / getValue(), but bleh…those are methods, not properties.

********************UPDATE************************

I have found that manipulating the DOM momentarily to get the outerHTML of an element can cause some problems when you’re doing alot of DOM manipulation simultaneously. Instead, the following method builds the HTML of the current element by looping through its attributes - much more reliable!

if (document.__defineGetter__ && !HTMLElement.outerHTML) {
    HTMLElement.prototype.__defineGetter__("outerHTML", function(){
        var emptyTags = {
		   "img":   true,
		   "br":    true,
		   "input": true,
		   "meta":  true,
		   "link":  true,
		   "param": true,
		   "hr":    true
		};

		var attrs = this.attributes,
		tagName = this.tagName.toLowerCase(),
		str = "<" + tagName;
		for (var a = 0; a < attrs.length; a++)
		  str += " " + attrs[a].name + "=\"" + attrs[a].value + "\"";

		if (emptyTags[tagName])
		  return str + "/>";

		return str + ">" + this.innerHTML + "";

    });
}

Alfresco Edit/Preview behind firewall

Posted by stephen on November 4th, 2008

After puting our servers behind a new Cisco firewall, we ran into a problem with the Alfresco Virtualization Server where you could either edit and not preview, or preview and not edit. Editing was only an issue where the Web Form content type was pulling in files on the AVM drive. In this case it was an included XSD.

The reason for this is that the preview goes out to ip.alfrescodemo.net to resolve the URL, which externally accesses the virtual server on port 8180. When you click edit, the IP in virtual-server.properties is used to access the AVM drive externally. With a strong security model, that’s obviously not going to happen. So, when I changed the virtual server IP to the local 192.168 base, editing worked, but previewing didn’t. With the public IP, it was the opposite.

Even allowing access to all ports for specific IP’s wasn’t working - still not sure on why that is. Adding the public IP to the Advanced TCP/IP settings of the box

It’s not clear yet whether this causes conflicts with other setups, but it did the trick for us! Editing and previewing works like a charm.

This is the solution for KTBU-21.

Website Core Toolkit (Standard)

Posted by stephen on October 26th, 2008

This framework is used for sites with the ability to be managed through Adobe Contribute. Adobe Dreamweaver will be used as the primary HTML production/site management tool.

File Naming and Directory/File Structure

File Naming

It is imporant to have a consistent file naming convention. I have had too many hang-ups caused by capitalization, so I opt to use all lower case characters, with dashes as seperators. Keep the name from 1 to 5 reasonably sized words. We want them to be understandable, but not clunky.

Root Directories

Being familiar with a common structure in your sites can save alot of time when working with all of the files. First we need our core directories, whos files will be used throughout the site.

  • /common/All site-wide CSS, Javascript (JS), image, and include resources should be placed here, respectively.
    • /common/css/
    • /common/images/
    • /common/include/
    • /common/js/
  • /templates/Dreamweaver watches the .dwt files in this directory and updates the dependant pages as neccessary.

Contextual Directories

Each of our sections is placed in its own directory, including the Home Page. A consistent directory structure is most helpful to a tool like jQuery.navActives in finding direct, parental, and child matches. Descriptive folder/file names also help with a number of things, including search indexing. The following is a example directory structure with a given set of sections:

  • Home Page/home/
  • About Us/about/
  • Products and Services/products-resources/

Each section/sub section directory should have /images/ and /documents/ folders. This keeps the images and documents used by that section local to those pages, and more mobile. This is also an Adobe Contribute default location for images or docs placed in a page by a content editor.

Head Resources and Template Content

Head Resources

The head resources are anything that will go in the <head> tag of the page. It is a good practice to have at least one site-wide resource file that is included in your base template. In this framework, that file is at /common/include/resources.htm. It includes site-wide meta tags, CSS <link> tags, JS <script> tags, and javascript init code such as a jQuery document.ready function.

Template Structure

I usually start with a base template, located at /templates/base.dwt. This defines the header, footer, and main area of the page where the other templates will be nested.

Header and Footer

Reusability and efficiency are key to a proper framework, so I use Server Side Includes (SSI) wherever appropriate and possible. The header and footer are prime examples. Even with Dreamweaver Templates, updating static HTML in a .dwt file would require updating each individual dependant page. By using include files, I can edit one file and the change is reflected wherever that file is included. This saves alot of time when file transfer is involved, and leaves less room for error. The header and footer are located at /common/include/header.htm and /common/include/footer.htm.

CSS and Javascript

CSS Stylesheets

ID selectors should generally be [A-z][1-9] characters only, and class selectors should be seperated by dashes, with no caps (like our file naming convention). An example of a selector with an ID and class name is:

#siteContainer .container-inner{}

IE does not recognize @import media types, so stylesheets with a specific media type need to be defined in the <head>.

I need to do some testing on the @media CSS encapsulator. If that works, media type defnition can be handled from within relevant stylesheet and all CSS references can be made from within site.css, but until then…

  • site.cssContains @import references to all primary style sheets. By default, the following:
    • base.cssContains resets and other base CSS properties for default rendering.
    • structural.cssContains styles for things like header, footer, page layouts.
    • modules.cssContains styles for content modules like secondary nav, sidebars, content containers, or the child elements thereof.
    • contribute.cssFor sites managed with Contribute only, this file should only list selector names of styles you want made available to Contribute editors. Allow properties to inherit from the other stylesheets for maintainability.
  • axs.cssContains styles for accessibility. Minimal testing has been done with this stylesheet, but I make it available in case it’s needed.
  • print.cssContains styles for print media.
  • ie.cssThis is referenced via conditional comment tags recognized only by IE. (e.g. <!–[IF IE]> <![endif]–>).

Javascript

The file at /common/js/site.js holds common functions or variables used throughout the site. For instance, this is where the indexOf() function is defined for IE.

SWFObject

Ever since the “click to activate” Active X update was made to IE, I’ve used SWFObject. I still prefer it to the ActiveContent.js generated by Flash and Dreamweaver. An example is at /samples/flash.html.

jQuery

The jQuery library is an essential part of this toolkit. At first I was afraid these JS libraries would complicate markup and create even more cross-browser issues. Anyone who’s worked with JS knows the struggle. But Unobtrusive Javascript libraries like jQuery seem to have a good handle on those issues, and I’m becoming increasingly comfortable with this black magic. Errors are handled gracefully, browser issues are minimal, and with their selector syntax, it does not complicate the markup at all. That being said, here are several of the core plugins I use and why:

  • jquery.meshtml I created this to mesh a string of HTML and existing element(s) on the page. Primarily used for arbitrary elements needed for advanced styling, such as complex containers and rounded corner containers.
  • jquery.selectorInspectorI created this to quickly generate CSS selectors based on the hierarchy of any given HTML. The indentation of the selectors respects that hierarchy, and you can step through each selector to confirm its specificity.
  • jquery.navActivesI created this tool to examine a specified list of anchor tags and compare them to a given URL, and return a list of parent, child, and exact matches.
  • jquery.navigenI’ve not created this yet. It’s purpose will be to create single or multi-level navigation out of unordered lists. It will use meshtml for the required element structure and navActives to handle active, parent and child links.

  • jquery.selecteSizerThis fixes the problem with IE not expanding the dropdown list so that the entire option is visible.

jQuery.navActives - Navigation Highlighting

Posted by stephen on October 26th, 2008

Nav highlighting can be a little tricky. It usually gets tricky when there is more than one level of secondary nav.

How do we know to hide or show sublinks, and how do we know when to highlight an associated “Overview” link? Or, what if your page is not in the left nav links, but you still want to highlight its parent? The list of caveats grows as you try to answer those questions, so I set out to make something that applies to these squirrely situations, instead of having to write a custom function each time.

jQuery.navActives scans through a specified list of links and finds the clostest matches to a given url (generally the current address - window.location.href).

Usage

j("#yourlinkcontainer a").navActive(options, callback(this, activeLinks, highestMatch));

The call back function is passed 3 parameters. The jQuery object calling the callback (this), an array of the matching links (activeLinks), and the highest match rigidity found (highestMatch).

Like my other plugins, we need to pass it some settings. Here’s an example of what I use for some primary navigation:
var mainNavHighlight = {
matchUrl:currentPageUrl,
alterElements: [{element:"each", className:"active"}],
rigidity:[1]
};
j(”div#siteHeader a”).navActives(mainNavHighlight);

Notice the rigidity. That is basically the number of items past the main URL to compare. That is one reason I have the Home Page in a “/home/” directory.

It is important that you organize ALL sections and sub sections in their own folder. Here’s a complex example of a navigation list that has parent and sub lists:

var sideNavHighlight = {
matchUrl:currentPageUrl,
alterElements: [{element:"each", className:"current"},
{element:"each.parents('ul:first').parent().children('a')", className:"current"},
{element:"each.siblings('ul:first').find('a:first')", className:"current"}],
rigidity:[1,2,3]
};
j(”div#sideNav a”).navActives(sideNavHighlight, showActiveSubs);
function showActiveSubs(i, list, high){
jQuery.map(list, function(n){
if(n.rigidity>=2){
jQuery(n.link).parents(”ul”).css(”display”,”block”);
}
});
}

Notice the alterElements setting, the rigidity, and the showActiveSubs function that uses the list of matched URLs returned by navActives.


Copyright © 2007 Stephen Rushing. All rights reserved.