Building a jQuery UI Dashboard

Sometimes you have an existing website or template that needs to function as a dashboard. The only solution then is to create drop-in widget functionality without necessarily modifying the HTML. This is a bit of an update to a much older post

The good news is that if the layout is by and large uncluttered and follows a predictable set of elements (key issue), it’s fairly straightforward to give a layout dashboard functionality without actually touching the HTML structure. jQuery and jQuery UI are perfect for this.

The tricky bit is adding any additional elements for control while handling each segment of content as a widget. In this case, every “widget” can be any element that has the CSS class column applied to it which means we can use the existing <h5> title element as the header for it.

Take as an example, my Simply theme which has no “widget-like” functionality whatsoever and the only JavaScript is Modernizr for backwards compatibility since it was recently rewritten in HTML5. But because all the content elements are predictable (there are sections and one level of nested sections that act as columns), it can be turned into a dashboard without touching the rest of the HTML.

You can take a look at a running demo of it and see below for an explanation of what’s going on.

A running demo of "Simply" turned into a dashboard

For this example, I needed the dashboard widgets to be able to be moved around on each row, have the title, content and the article icon changed and of course be able to close them. Naturally, this is only a client-side example. The final version would need to post these changes server-side for saving.

The Procedure

We can first start by adding the jQuery UI theme CSS file right above the original stylesheet. I’m using a customized version of the Smoothness theme. Just a few tweaks to make it match mine.

<link rel="stylesheet" type="text/css" href="themes/jQueryUI/ui-style.css" id="ui-theme" />
<link rel="stylesheet" type="text/css" href="themes/Simply/style.css" id="theme" />

After that we can add the jQuery and jQuery UI libraries to the template. In my project, there was also a need to add a wysiwyg and I had to use their propietary one, but for this example, I can simply use TinyMCE.

For this example, I’m going to put all the scripts into a folder called /lib. This is also where I’ll extract TinyMCE (the jQuery plugin version).

Modernizr, very usefully, can load other scripts by URL, so we can simply do the following right after adding its JS own file :

<script type="text/javascript" src="lib/modernizr.custom.js"></script>
<script type="text/javascript">
	Modernizr.load([{
		load: "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"
	}, {
		load: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"
	}, {
		load: "lib/tiny_mce/jquery.tinymce.js"
	}, {
		load: "lib/dash.js"
	}]);
</script>

And that’s all we need to do on the template itself. That last file being called, “dash.js” in the /lib folder is where we’ll put everything else.

dash.js

I’m going to start by adding some custom CSS to this file rather than cluttering up the head section of the template. These are just a few added classes to make the content more presentable.

All this will be added inside the jQuery $(fuction() { }); section.

$('head').append('<style type="text/css">' +
	'.icon { width:75px; height:75px; }' +
	'.ticon { float:left; margin: .3em .5em; width:25px; height:25px; box-shadow:1px 1px 1px #aaa; }' +
	'.ui-dialog { text-align:left; }'+
	'.ui-dialog input[type="text"], .ui-dialog textarea { display:block; width:90%; }' +
	'</style>');

Next, since we’re working with TinyMCE, we’ll store those settings in a variable.

var tinySettings = {
	script_url : 'lib/tiny_mce/tiny_mce.js',
	plugins: "inlinepopups",
	theme: "advanced",
	theme_advanced_buttons1 : "bold,italic,underline,strikethrough," +
		"separator,cut,copy,paste,separator,bullist,numlist," +
		"separator,undo,redo,separator,link,unlink,image",
	theme_advanced_buttons2 : "",
	theme_advanced_buttons3 : "",
	theme_advanced_toolbar_location : "top",
	theme_advanced_toolbar_align : "left",
	theme_advanced_statusbar_location : "bottom",
	width: "100%",
	height: 250,
	content_css : "content.css",
};

You’ll notice the last line refers to a “content.css” file. This basically styles the wysiwyg content as it’s being edited. In my case, this is all it contained…

body {
	font: normal 90% Sans-serif,tahoma,verdana;
	color: #575757;
}

p {
	line-height:170%;
}

Dialogs

Dashboards frequently use dialogs when making changes, presenting confirmations etc… In this case, I just created two dialogs for editing content and to confirm when removing the widget…

function initDialogs() {
	// Close widget dialog
	$('article').append('<div id="dialog-confirm-close-widget" title="Close widget" style="display:none;">' +
		'<span class="ui-icon ui-icon-alert" style="float:left;"></span>' +
		'You are about to delete this widget. Are you sure?</div>');

	// Config widget dialog
	$('article').append('<div id="dialog-config-widget" title="Modify" style="display:none;">' +
		'<form><fieldset><legend>Change widget content</legend>' +
		'<p><label>Title <input type="text" id="widget-title-text" /></label></p>'+
		'<p><label>Content <textarea rows="5" cols="50" id="widget-content-text">Some test content</textarea></label></p>'+
		'<p id="icon-field"><label>Icon URL <span>Will be resized to 75x75 pixels</span><input type="text" id="widget-icon-text" /></label></p>'+
		'</fieldset></form></div>');

	// Create and destroy these dialogs to hide them
	$('#dialog-confirm-close-widget').dialog("destroy");
	$('#dialog-config-widget').dialog("destroy");
}

That textarea will be replaced with TinyMCE later.

3 Little Helpers

Often overlooked, helpers can save a lot of time and help unclutter your JS quite a bit.

The following three check for null or empty strings, initializes and applies TinyMCE to an element with the given settings and removes the wysiwyg after sending the changes back to the textarea… in that order.

function notEmpty(t) {
	if(t) {
		if($.trim(t) != "")
			return true;
	}
	return false
}

function initTiny(t, s) {
	t.tinymce(s);
}

function closeTiny(t) {
	var e = t.attr('id');
	t.val(tinyMCE.get(e).getContent());
	tinyMCE.execCommand('mceRemoveControl', false, e);
}

Widget controls

In jQuery UI, there are ui-icons that we can use here without having to invest extra time to create our own. This also means we can apply our controls to these icons by using something like the “title” or “alt” attribute as the command string. In this case, I opted to use the “title” attribute since I’ll be using these icons in <span> tags.

function setControls(ui) {
	ui = (ui)? ui : $('.ui-icon');
	ui.click(function() {
		var b = $(this);
		var p = b.parentsUntil('.column').parent();
		var i = p.children('img[alt="icon"]:first').eq(0);

		var h = p.children('.ui-widget-header h5:first').eq(0);

		// Control functionality
		switch(b.attr('title').toLowerCase()) {
			case 'config':
				widgetConfig(b, p);
				break;

			case 'toggle':
				widgetToggle(b, p, i);
				break;

			case 'close':
				widgetClose(b, p);
				break;
		}
	});
}

Note, the three widget functions that will “config” (as in edit the content), “toggle” (collapse/expand) and “close” the widgets.

The toggle function is pretty simple. I wanted to minimize the .widget-content class (which we’ll add later), change the minus into a plus on the side of the widget after collapsing and turn the big image icon seen on most of those segments into a smaller one that fits on the header. The .ticon class is what we added to the head earlier.

// Toggle widget
function widgetToggle(b, p, i) {
	// Change the + into - and visa versa
	b.toggleClass('ui-icon-minus').toggleClass('ui-icon-plus');

	// Turn the big icon into a small one or visa versa
	if(i.hasClass('icon'))
		i.switchClass('icon', 'ticon', '300');
	else
		i.switchClass('ticon', 'icon', '300');

	// Show/Hide widget content
	p.children('.widget-content').eq(0).toggle(300);
};

Closing a widget is also pretty straightforward. We’ll be using the closing dialog we added earlier.

// Close widget with dialog
function widgetClose(w, p) {
	$("#dialog-confirm-close-widget").dialog({
		resizable: false,
		modal: true,
		buttons: {
			"Close widget": function() {
				p.toggle('slide', {}, 500, function() {
					p.remove();
				});
				$(this).dialog("close");
			},
			Cancel: function() {
				$(this).dialog("close");
			}
		}
	});
}

The last of the widgets is the config. This is the trickiest part, since we’ll need to create and destroy TinyMCE instances and get input from the dialog and push to the content.

// Modify widget
function widgetConfig(w, p) {

	// Input elements in the dialog
	var dt = $('#widget-title-text');
	var dc = $('#widget-content-text');
	var du = $('#widget-icon-text');

	// Widget elements to change
	var wt = p.children('h5:first').eq(0);
	var wc = p.children('.widget-content').eq(0);

	// If there is no icon on the widget, there's nothing to change
	var wi = p.children('img[alt="icon"]:first');
	if(wi.length > 0) {
		wi = p.children('img[alt="icon"]:first').eq(0);
			$('#icon-field').show();
	}
	else {
		$('#icon-field').hide();
	}

	$("#dialog-config-widget").dialog({
		resizable: false,
		modal: true,
		width: 500,
		open: function() {
			if(wi != null)
				du.val(wi.attr('src'));

			dt.val(wt.text());
			dc.val(wc.html());

			// Initialize TinyMCE
			initTiny(dc, tinySettings);
		},
		buttons: {
			"Save changes": function(e, ui) {

				// Some widgets don't have an icon
				if(wi.length > 0) {
					if(notEmpty(du.val()))
						wi.attr('src', du.val());
				}

				// Remove editor (also gets content from TinyMCE back to textarea)
				closeTiny(dc);

				// Update
				if(notEmpty(dc.val()))
					wc.html(dc.val());

				// Careful here, don't wanna lose the control icons
				if(notEmpty(dt.val())) {
					var ci = wt.children('span.ui-icon');
					wt.html(dt.val());

					// Reset controls
					wt.prepend(ci);
					setControls(ci);
				}

				$(this).dialog("close");
			},
			Cancel: function() {

				// Destroy TinyMCE
				closeTiny(dc);

				$(this).dialog("close");
			}
		}
	});
}

Basically, the above function grabs the text fields from the dialog and puts the existing content on page into them. The user makes some edits and clicks on “Save changes” which causes the script to take the changed content and apply it back to the page. In a real-world example, this content will actually be posted back to the server.

Note: Since the control icons are also located in the title, when we change the title text, we have to make sure that the control icons are spared from any edits.

The Initialization.

This basically takes every section with a “column” class that has a <h5> element inside into a sortable widget. It adds the control icons as <span> tags and wraps the content in a widget-content <div> (which we can minimize) and adds the ui-widget classes from jQuery UI to aid with sorting.

function Init() {

	// Initialize dialogs
	initDialogs();

	// Portlet and sort related CSS classes
	var sortClasses = "ui-widget ui-widget-content ui-helper-clearfix";

	// Set every column segment with h5 element as a sortable widget
	$('.column:has(h5)').each(function() {

		var s = $(this);
		var p = s.parentsUntil('section').parent();
		var h = s.children('h5:first').eq(0);

		if(!p.hasClass('ui-widget'))
			p.addClass(sortClasses);

		// Function icons
		h.addClass('ui-widget-header')
			.prepend('<span class="ui-icon ui-icon-gear" title="Config"></span>')
			.prepend('<span class="ui-icon ui-icon-minus" title="Toggle"></span>')
			.prepend('<span class="ui-icon ui-icon-close" title="Close"></span>');

		// Need this to drag not highlight
		h.disableSelection();

		// Interaction cues
		h.css('cursor', 'move');
		$('.ui-icon').css('cursor', 'pointer');

		// Wrap control stuff (like icons and headers) in a widget-header div
		// and the rest in a widget-content div
		s.children().not('img[alt="icon"], .ui-widget-header, .ui-icon')
			.wrapAll('<div class="widget-content" />');

		s.children().not('widget-content').wrapAll('<div class="widget-header" />');
	});

	// Group sortable widgets in each section to one sort-area
	$('section').has('.column').each(function() {
		$(this).children().not('header,hr')
			.wrapAll('<div class="sort-area" />');
	});

	// Trigger control initialization
	setControls();
}

And lastly, if we don’t have sorting ability, we can’t really call this a dashboard. Ironically the most defining characteristic in dashboards is the most simple to implement once all the wrapper divs are in place.

$(".sort-area").sortable({
	connectWith: ".sort-area",
	opacity: 0.6,
	helper: 'clone',
	dropOnEmpty: true
});

This post was prompted by yet another example of why I have a dim view of project managers.

Can you put together a dashboard for us?

OK, no problem.

We already have the backend completed and a template in place although it doesn’t have any widget markup. Except for the head tag, we need you to not touch the rest of it because we do have other stuff on the page. BTW… We need it in an hour.

@$#& me!

I wish the above was an exaggeration, but sadly, it happens more often than I care to tolerate.

The problem is easy enough to come by. Someone first builds a backend thinking they’ll have all sorts of widget like functionality in the future, but doesn’t develop the front end for any of it. Time goes by… The next developer comes along and decides that since the template is already in place, they’ll finagle the output to be more “widget-like” and create a separate handler to manage user changes. This also explains why so many “legacy” dashboards are notoriously slow.

So now they bring in someone else to drop in a dashboard where there previously existed no frontend for it… And they want it yesterday.

Oh, right New Year resolution…
Less grumpy. Less grumpy. Less grumpy.

Update

Changed the notEmpty(t) function line if(t != “”) into if($.trim(t) != “”).

Advertisements

Web Minimalism and Sucking at it

The original title to this post was “Everything New, Sucks”, but I thought I’d be nicer as to not take a complete 180 considering my last post. My New Year resolution to be less grumpy is looking shakier by the day.

Beyoncé style  = Whitney code?

I’m truly dismayed how some of the seemingly beautiful layouts are actually courtesy of HTML, CSS and JavaScript that look like the residue in a crack pipe. If the front page of your site is drafting more hobbled scripts than the average backend dashboard, then you’ve got problems son. It’s almost like no one really cares how it’s all put together as long as it looks good.

I know minimalism is the new in-thing in web design, almost to the point of being outright confusing or silly, but can we at least extend the sentiment to the scaffolding too? Or else, it kinda seems stupid, you know, hypocritical.

I also come across a lot of designers who quite loudly espouse their work is only meant for “modern” browsers and will actively block access from older browsers rather than encouraging them to upgrade or change them. The excuse is that older browsers “break” their carefully carved patchwork of copypasta.

“Break”?

If it makes your site unusable, go back and fix your work. It’s called pride in workmanship, you misanthropic troglodytes!

I know its fashionable to rag on the browser, but people aren’t going to change their browsers because your work doesn’t work on them. Deal with it.

Can I read the text? Can I see the images? Can I still “use” the site? Does your site have actual “content” that I can still consume? MOST websites will work in older browsers, even HTML 5 ones, once you stop giving it meaningless improvements, and “breaking” is acceptable as long as the content is still accessible. If you absolutely have to use the new functionality for something, then at least use Modernizr to make it usable on older browsers.

Which brings me to…

The HTML 5 Bug

Now slightly more infectious and deadly than H5N1. I’ve got nothing against it, but it seems it’s being abused by the same mindset that made the bad ol’ days of HTML 4 almost as unbearable. The fact that HTML 5 is new and hip doesn’t mean you stick every new tag in your design inappropriately and call it progress. The draft isn’t even finished yet and I’m already seeing designers making ridiculous uses of the footer, header and map tags.

I.E. I’ve  seen <map> still being used being used as a navigation bar instead of… and here’s a shock… the bloody <nav> tag. I’m sorry, is this 1999? Ah, but it’s OK, because the rest of the page is HTML 5.

<map> Is meant to be used for image maps and can designate clickable areas on an image for page interaction like photos identifying people.  I can accept that HTML 5 is here to stay and there’s nothing that can be done about it, but at least learn to use it properly!

I wouldn’t be this upset if it weren’t for the fact that the above abomination was the work of a “professional web designer”. Well, I’m not a professional web designer and even I know this. And if you still can’t get it, well then maybe you should be doing something else… like making soap.

Quit scripting your sites to death

There’s a post by Jim Dalrymple made last month that really puts into perspective how much rubbish is loaded asynchronously after the content itself has arrived. All the extra bloat just bogs down the browser until everything else arrives so even if the content is there, you can’t scroll to read it.

The real kicker is that the biggest load on that list was BGR and that’s a “minimal” layout. “The Biggest Letters in Tech”? Apparently.

And most of these aren’t even useful, but rubbish like Avalanche meant to drive traffic to content devoid of content. The rest are poorly engineered statistics widgets that have better use as browser stress tests than gathering user data on a production site. Or are ad widgets. These scripts are massively inefficient because they’re doing a lot of server-side work client-side in an effort to offload some of the burden on their own infastructure.

JavaScript isn’t C++ so quit treating it like so.

And what’s with designers and content managers not caring about what they include in the HTML sent to the visitor?

I checked the source on CNN.com and found the following buried in an HTML comment around line 200 :

	Last snapshot: 12/12/2011 11:30 AM
	IF auto generated include above stops working, uncomment the html below and manually update at hourly intervals:

<li class="pmNsStory">
    <div class="pmNsHeadline"><a href="http://www.cnn.com/2011/12/11/opinion/graham-debate-romney/index.html">Opinion: How Romney blew it</a></div>
    <div class="pmNsPopularity"><div class="pmNsPopImage" style="width: 98%"></div></div>
</li>
<li class="pmNsStory">
    <div class="pmNsHeadline"><a href="http://www.cnn.com/2011/12/11/sport/ryan-braun-drug/index.html">Spokesman: Braun will be 'exonerated'</a></div>
    <div class="pmNsPopularity"><div class="pmNsPopImage" style="width: 69%"></div></div>
</li>
<li class="pmNsStory">
    <div class="pmNsHeadline"><a href="http://www.cnn.com/2011/12/11/us/pennsylvania-paterno-hospital/index.html">Panama's ex-ruler Noriega returns home</a></div>
    <div class="pmNsPopularity"><div class="pmNsPopImage" style="width: 67%"></div></div>
</li>
<li class="pmNsStory">
    <div class="pmNsHeadline"><a href="http://www.cnn.com/2011/12/12/world/meast/syria-unrest/index.html">Ex-Penn State coach Paterno hospitalized</a></div>
    <div class="pmNsPopularity"><div class="pmNsPopImage" style="width: 59%"></div></div>
</li>

… Really?

And just for kicks, I went to MSNBC.com and discovered the actual “content” starts at line 716 :

And that's not even the real beginning.

Even after turning off word wrap, the first third of the source is nothing but inline JavaScript and CSS and so is a full half of the entire source.

Mind you, both are meant to be “minimal” designs far removed from their older, uglier, “less efficient”, versions.

I haven’t checked the mobile versions of these, but considering most people pay for their data plans by the MB, even if they’re half the size of their desktop counterparts, the designers of these and other bloated sites are doing a huge disservice to the very people who are paying their bills.

Any Browser is Dead. Long live bloat!

When did it become fashionable — respectable even — to design websites so poorly and with so much extra rubbish that it would be unusable? Forget people with special needs, I mean the average Joe with a decent computer, modern browser and a good internet connection would be in imminent danger of a face-full of glass shards or plastic splinters if he even dares to visit some of these sites.

The common excuse is that, well, you have to make sure that the site’s added features need a lot of capabilities that aren’t available on older browsers. I have news for you: The web has been around for longer than some of you designers have been alive and the core use is still the same. To consume content.

The vast majority of effort on any site should be on the backend. The core architecture, the security, the control panel, the security, the database, the security. Sort these out first and you will find yourself spending less time needing to make UI improvements. A good workflow will encourage a good UI.

Fluid, variable width columns in CSS

A little while ago, I made an addendum to one of my themes trying to prove a point that displaying a lot of content is possible without major HTML and CSS surgery.

But in retrospect, the approach I took, creating a separate “segment” class and having separate “content” and “sidebar” classes, seems a bit silly. It clearly violated the DRY principle since I was overlooking one important fact…

“Content” and “Sidebar” are basically columns of different width

Once I finally realized this fundamental aspect of layout presentation, then all else fell into place. Now there is no need for a “full” class since any content that doesn’t belong to a column automatically takes up the full width.  The rest of the content can be displayed by using percentages relative to the full width of the container.

The Content and Sidebar sections can now be realized by applying a 2/3rds width and 1/3rd width columns respectively and other percentages can be used to mix and match columns on any page segment.

Previously, there was also an “extras” div that was inherited from the Mod Portal theme from which this all was derived. Now this too was eliminated by using three 1/3rd width columns in a separate “body” segment.

You can see the final result of the changes and it is proportionally lighter in CSS and HTML than with the previous iteration. I also added a login/registration form since that was also originally part of Mod Portal, increased the font sizes in the navigation bar, changed some of the colors for clarity and added comments in both the HTML and CSS.

Presenting a lot of content without killing children or small animals

Three things that will help almost anyone* publish a lot of content on one page without causing aneurysms in, by far, one of the shortest posts I’ve done in a while.

Abstracts are your friend

You can call them summaries, blurbs, excerpts or abstracts, but it all does the same thing. I’m visiting a site to see if it’s interesting and the only way to know for sure is I get a good summary of it in under 30 seconds. So go ahead and flood the front page if you must, but you need to summarize!

You’ll be amazed at how much content you can get away with publishing on one page with just excerpts, and we’re talking 10 articles or more along with a sidebar with links and even more content beneath.

Make the content look like it’s not a lot and you can still get the message across and entice visitors to stay longer.

Whitespace! Whitespace! Whitespace!

If you need to push a lot of “stuff” to chew on the front page, give your visitor something to drink to help it all go down. It’s nice to think your visitors can read content the same way a barcode scanner works at the super market, but this is the real world and we’re all slow and impatient humans.

Too much content in a small space makes it just that much harder to understand or follow. I’ve lost count how many times I’ve had to say this, but we don’t read online the way we read old media. Scanning get’s the gist through and if the content is interesting enough, they’ll stay and read more.

More content ≠more HTML and CSS

I’ve had to create entire frontends for clients and one familiar excuse for putting it off and keeping old, broken, templates that some project managers think the more content you have to display, the more HTML and CSS needs to be worked into the new layout.

I call BS, good sir! It was because I got this excuse again that I finally decided to write about it.

Unless you’re designing a rich UI application or something with WebParts (blegh!) or publishing pages and pages of code (*cough*), there’s no reason more content should disproportionately add to the download size and complexity of the theme. More content should be just more content. If formatted properly, there’s no reason it would mess up the rest of what’s on the page.

Case in point, I just hacked in more content to my Simply theme with small changes to the CSS and minimal changes to the HTML scaffolding. You can see what it looked like before adding more content on its original post.

That change took less than 5 minutes and it shows everything I’ve written here so far. That’s 10 articles with the 2 most recent featured with 1 welcome banner, 1 sidebar, 2 link lists using said sidebar and one of 3 extra columns on the bottom, along with an about page and tag cloud. That’s a lot of content (granted without JavaScript) in HTML all under 10K in download size less images and with plenty of room to add more.

*And now, since it’s 1AM right now and I just finished my futile explanation via email to my boss on why the layout we’re using is rubbish (since they chose to ignore all of the above) and completed this post… I’m going to bed!

Simply: A stripped down blog theme

I’ve had a few requests lately for a stripped down and simplified version of the Mod Portal, but didn’t get a chance to create one until recently. One request from Jacob asked for it to have “not as many features”… I guess that’s a nice way of asking for something that didn’t include the kitchen sink ;)

Mod Portal was originally written as part of a design tutorial almost three years ago and really did morph into a CMS template. Although it also includes a page variation for a blog (a post index), there were a lot of added stuff in the CSS and the html that wasn’t needed for a blog.

So, here’s a theme based on Mod Portal with none of the fluff. It’s designed to have the absolute bare essentials for a blog or personal site, contains fonts and sizes that are easy to read and very lightweight in CSS and XHTML. A lot of the base still came from Mod Portal, but this one doesn’t even have any JavaScript. This is by far the simplest theme I’ve done in a while.

Simply screenshot