We've moved!

TechKnack.blogspot.com has officially moved to TechKnack.net. You should be redirected in 3-5 seconds. Thank you.
Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

April 1, 2008

Got Mad Coding Skills?

Add this post to Del.icio.us. Del.icio.us (0 saved)

Think you got what it takes to slice up a PSD file into a valid and usable (X)HTML template? Then check out CSS Off, a contest that gives you the opportunity to do just that. At one minute past midnight CST on April 5, a PSD file will be uploaded for contestants to download. Contestants will have up to 24 hours to submit their completed entry.

Don't have Photoshop? Check out the GIMP! I'm hoping that the PSD file is GIMP-compatible. You could try to convert it one way (using an online service) or another (directly in the GIMP),but there's always the issue of unsupported layer effects getting messed up.

And, no, this is not an AFD trick. At least, I hope it isn't...

February 12, 2008

Progressively Enhance copyable code blocks

Add this post to Del.icio.us. Del.icio.us (0 saved)

I've employed a bit of progressive JS here on the blog to make it easier for readers to copy the blocks of code that I post. Go ahead, try it: click within the code block, and the entire thing is selected. Makes for pretty easy copying, rather than having to carefully select just the text inside the box...
// I'm code!! Copy me!! $ hello world // w00t...

The way it works

Here's how it works: I've defined a function which goes over every single <code> element on the page. For each code element, a textarea element is created and inserted into the DOM tree directly before the corresponding code element. Once a textarea is added to the DOM, it is hidden via CSS. The textarea is given, as child nodes, recursive clones of code's DOM child nodes. Then, the onclick event of the code element and the onblur event of the textarea are set to different functions. The code's onclick function hides the code element, un-hides the textarea element gives the textarea the focus, and selects the textarea's contents. The textarea's onblur function hides the textarea and un-hides the corresponding code element.

It's possible to have the script do this for all code elements on the page, but I've implemented it to only target those code elements to which I've given a class name of "copyable".

The CSS

The hiding-un-hiding tricks aren't possible without CSS. Well, they're possible, but such an implementation would make the JS much messier. Here I've used CSS to similarly style both code elements and elements with a class name of "codeenhance" (which is used to identify the inserted textareas).
.codeenhance, code { width:430px; color:#000; display:block; background:#eee; padding:0 10px; padding-bottom:1em; margin:0; border-width:3px 1px !important; border:#99f solid; overflow:auto; line-height:1.4em; font-family:monospace; font-size:1.1em; } .codeenhance, .copyable { height:250px; } code { white-space:pre; } .codeenhance { padding:0; } .codeenhance-off { display:none; } The first declaration is the main styling for the code elements. The height specification for .codeenhance and .copyable fix the height of copyable code blocks, so your 159 lines of copyable JS don't take up too much space. I've left the height out of the main declaration, to allow the other code blocks to expand/retract to their contents' size. The white-space declaration for code elements maintains the whitespace as you typed it. The padding declaration for .codeenhance fixes an interesting jumping issue with the textareas. And the display declaration for .codeenhance-off is what hides the appropriate items.

The Javascript

Here's the JS. The explanations are in the code comments ;)
// Enable click-select on code elements function enhanceCode() { // Get all code tags var codes = document.getElementsByTagName("code"); // loop over code tags for (i=0; i<codes.length; i++) { // working only on tags of class "copyable" if (codes[i].className.match('copyable')) { // for each tag, create a new textarea var text = document.createElement("textarea"); // copy the code's children over for (j=0; j<codes[i].childNodes.length; j++) { // special code for Blogger, to take care of extra br elements if (codes[i].childNodes[j].nodeName == "BR") text.appendChild(document.createTextNode('\r\n')); else text.appendChild(codes[i].childNodes[j].cloneNode(true)); } // set the initial classname // (use .className rather than .setAttribute('clas', 'blah') because IE6 // doesn't like the latter text.className = "codeenhance codeenhance-off"; // setup the onblur event text.onblur = decodex; // insert the textarea before the code codes[i].parentNode.insertBefore(text, codes[i]); // setup the onclick event codes[i].onclick = codex; } } } // this = code element; this.previousSibling = textarea element function codex() { // show the textarea this.previousSibling.className = this.previousSibling.className.replace(/ ?codeenhance\-off/, ""); // hide the code this.className = this.className+" codeenhance-off"; // focus and select the textarea this.previousSibling.focus(); this.previousSibling.select(); } // this = textarea; this.nextSibling = code function decodex() { // show the code this.nextSibling.className = this.nextSibling.className.replace(/ ?codeenhance\-off/, ""); // hide the textarea this.className = this.className+" codeenhance-off"; } window.onload = function() { // run the enhancement /after/ the window has loaded enhanceCode(); }

February 1, 2008

A well-designed website: many things to many people

Add this post to Del.icio.us. Del.icio.us (0 saved)

In my Advanced Web Design class at school, my professor asked us to post what we thought defined a well-designed website. I thought I'd share my answer here. I'm sure he wasn't expecting anything so involved as this :D

"What makes a well-designed website?" That's quite a question.

As a dabbler in web design, I would say a well-designed website is "good-looking". It should look somewhat elegant. The "Web 2.0 style" -- glossy buttons, spacious openness, minimalistic simplicity, and copious use of gradients -- comes to mind. I can't think of any sites that strictly adhere to all these points, but DynamicDrive is a good-looking site.

As an HCI-minded individual, a well-designed website is made by more than its layout. A well-designed website makes it obvious what the site's purpose is, what can be done on the site, how to go about doing it, etc. Again, simplicity is king, and clutter is sin. Content is easy to skim and understand. The layout does not make it difficult to navigate. If a feature breaks, the site will either gently tell the user so, and make it easy for the user to report the breakage; or the site will function in such a way that the user doesn't notice the difference, and the developer will be alerted. I think Digg is a fairly usable, well-designed site.

As a web developer at heart, a well-designed website is all in the code. Separation of structure, content, presentation, and functionality is something to be strived for. It should be built as follows:

  1. First build a mock page (with the final site's design in mind) which contains either mock or real content, and only structure-oriented HTML markup. Presentational markup is sin :) This page is not design-oriented; its only purpose is to structure the page's content in a semantically meaningful way. Any tags that do not directly relate to the structure and/or content of the page should be struck down with a flaming sword of death. Organize things in such a ay that they flow logically along the page.
  2. Develop the CSS in an externally linked file. Introduce any design-specific images through the CSS, if possible (and it should be possible). Strive for cross-browser compatibility within the CSS, making the site look as similar as possible from one browser to the next. If any additional markup is required for the design of the site, the CSS should be developed to make the site look good without it; if still necessary, the necessary markup can be added through the next step. Use what you have to get what you want.
  3. Develop the functionality (using JavaScript) in another externally linked file. Nonintrusive javascript should be used, progressively enhancing the site after it has loaded. Nonintrusive javascript should also be used to add any necessary additional markup to be used by the CSS. Again, use what you have to get what you want.
  4. If possible, convert the finished page layout into a template, and use a server-side language to fill it with dynamic content.
  5. Finally, when all is said and done, pull up the page in a browser, and make sure everything works. Turn off javascript, refresh, and make sure everything works. Turn off css, refresh, and make sure everything works. Then browse the web for a while....and see if it works :)
According to these steps, a well-designed website will work across browsers, degrade gracefully in older browsers, respect the wishes of those with Javascript turned off. There are lots of websites that use this methodology (using only semantically-meaningful html, etc). One well-designed website in this case is Dynamix Labs. It doesn't seem that they use any javascript, and if you turn off CSS, the site is still very readable.

As a user, I want information, and I want simplicity in doing what I want to do. A lot of the time, I want information that I can talk about in my blog. I use google as my main go-to source for finding things. If I find a site that gives me good information on a regular basis, I want to be able to subscribe to its RSS feed, or something similar. If I find a site that I come back to on a regular basis (whether I be forced to, or do so of my own free will), I want to be able to customize my experience on that site. I want to be able to change the colors, the landing page content; anything and (usually) everything, I want to customize it to better serve my way of doing things.
One service that I use on a daily basis is Google Reader (you'll have to sign in to your google account), a web-based RSS aggregator. It features "folders" that you can use to organize your feeds, drag-and-drop re-ordering of feeds, and very easy-to-use methods of subscribing and unsubscribing from feeds. Another service that I use regularly is Netvouz, a social bookmarking site. I chose Netvouz over other bookmarking sites because of its folder organization feature. You can organize your bookmarks according to category (and sub-category) and make certain bookmarks private. It's also easy to delete or edit my existing bookmarks, and really easy to add new bookmarks using the Netvouz firefox extension.

I must say, though, that my most frequently used and favorite thing is FireFox. It is customizable in (almost) every way possible; its functionality is infinitely extendable through extensions, which anyone familiar with XML and Javascript can write. It has excellent support for standards; the upcoming FireFox 3 is said to have passed the Acid2 test, which is huge for web developers.

As you can see, from my point(s) of view, a well designed website can and should be many things to many different people.

So, what do you think? What makes a website "well-designed"? What are some examples of well-designed websites?

Note: References to certain things "being sin" should not be taken literally. Putting presentational markup on your page will not send you to hell. Nor will avoiding clutter send you to heaven. These are just my personal pet peeves :)

January 31, 2008

The JS behind "Most Popular Posts"

Add this post to Del.icio.us. Del.icio.us (0 saved)

Original article: Most Popular Posts list for blogger

In my last article "Most Popular Posts list for blogger," I presented my version of Chris Riley's Analytics API workaround. I also promised to go through the JavaScript for those who were interested :)

The JS

Without further ado, the code:
<div id="popularposts"><noscript>Please enable javascript to view the most popular posts list.</noscript></div> <script type="text/javascript"> <!-- function topcontentCallback(obj) { try { var url,title,output; output='<ul>'; for (i=0; i< obj.count; i++) { url = obj.value.items[i].url; try { title = obj.value.items[i].name[0].content; title = title.replace('<title>', ''); title = title.replace('</title>', ''); title = title.replace('Tech Knack: ', ''); } // if we didn't get the title (bad Pipe!), give a pseudo- // title from the URL catch (e) { title = url.substring(url.lastIndexOf("/")+1, url.lastIndexOf(".html")) .replace(/\-/g, " "); } //remove the <title> tags the pipe leaves in. output+= '<li><a href="'+url+ '" title="'+title+'">'+title+'</a></li>'; } output += '</ul>'; document.getElementById("popularposts").innerHTML = output; } catch (e) { var error = "Error fetching data: "+e; if (e.toString().indexOf('i')!=-1) error += "<br />i: "+i; document.getElementById("popularposts").innerHTML=error; } } //--> </script> <script src="http://pipes.yahoo.com/pipes/pipe.run?_id=bi6F5qPK3BGfvuxf1vC6Jw&_render=json&_callback=topcontentCallback" type="text/javascript"> </script>

The Div

<div id="popularposts"><noscript>Please enable javascript to view the most popular posts list.</noscript></div>
The div with id "popularposts" is the div whose content will be replaced with our list. It initially contains some noscript text for those without JS.

The Function

function topcontentCallback(obj) {
The topcontentCallback function is the function used to handle the JSON output by the Yahoo! Pipe. "obj" is the JSON string passed in by the Pipe.

try { ...... } catch (e) { var error = "Error fetching data: "+e; if (e.toString().indexOf('i')!=-1) error += "<br />i: "+i; document.getElementById("popularposts").innerHTML=error; }
We enclose the main processing code in a try block. Firefox2 and IE6 (and presumably 7 and 8) support try blocks in JS. This way, if there is a problem processing the data (a referrenced node is missing, etc) the user gets a nice little error message rather than a JS error thrown by the browser. In te case of an error, the previously mentioned div's content is set to "Error: ". Not useful to visitors, per se, but good for debugging your script. The "if (e.toString().indexOf('i')!=-1)" part simply appends i's value (from within the try block) to the error message. Again, good for debugging.

var url,title,output; output='<ul>';
Declare some variables, initialize the output variable (we use an unordered list for display).

for (i=0; i< obj.count; i++) { url = obj.value.items[i].url;
We loop over the items given by the Pipe. First we pull the url of the popular page.

try { title = obj.value.items[i].name[0].content; title = title.replace('<title>', ''); title = title.replace('</title>', ''); title = title.replace('Tech Knack: ', ''); } // if we didn't get the title (bad Pipe!), give a pseudo- // title from the URL catch (e) { title = url.substring(url.lastIndexOf("/")+1, url.lastIndexOf(".html")) .replace(/\-/g, " "); } //remove the <title> tags the pipe leaves in.
Here we try to pull the page's title, as pulled by the Pipe. I've noticed that, occasionally, the Pipe will have trouble getting the page's title, and so leaves the title blank, which doesn't play well with our script. Within the try block, I get the content of the title; I then try to replace (remove) the title tags and the site's title. If there is no title text from the Pipe, this will throw an error. In the catch block, we use the page's url to "guess" the title, removing the ".html" at the end using url.substring(), and replacing the intermittent dashes which replace punctuation and spaces with .replace(). These two commands are strung together to be url.substring().replace(). The result: if the article titled "CSS trick - two background images" doesn't have a title attribute in our JSON, the title is given as "css trick two background images". For this reason, you should enable "post pages" in your template's preferences.

output+= '<li><a href="'+url+ '" title="'+title+'">'+title+'</a></li>';
Here we append to our ul an li containing a link to the popular page.

} output += '</ul>'; document.getElementById("popularposts").innerHTML = output;
After the for loop, we add the final </ul> and replace the div's contents.

All this is just a function; without calling it, it is no good.

<script src="http://pipes.yahoo.com/pipes/pipe.run?_id=bi6F5qPK3BGfvuxf1vC6Jw&_render=json&_callback=topcontentCallback" type="text/javascript"> </script>
This imports the JSON created by the Pipe, telling it to render in JSON format (&_render=json) and to call our function to handle the JSON (&_callback=topcontentCallback). If you copy and paste the src to a browser window, you'll see that the content is simply a function call with the JSON as the parameter:
topcontentCallback({"count":7,"value":{"title":"TechKnack".......}});

January 29, 2008

Most Popular Posts list for blogger

Add this post to Del.icio.us. Del.icio.us (0 saved)

Last week I was looking for a way to have a "Most Popular Posts" widget here on blogger. The first and only true "widget" I found was Affiliate Brand's Most Popular Posts Widget. The only problems: it has a default color scheme that doesn't really go with that of my blog, and the height is pretty much fixed. It seems that you can change the look-n-feel with an external CSS sheet...but, unless you have another file host, that's a luxury we cheap Blogger bloggers don't have :)

Not satisfied with AffiliateBrand's widget, I kept searching. To no avail; that was simply the only pre-packaged blogger widget out there. Then the thought occurred to me to use my Google Analytics data to roll my own pseudo-widget. Genius, no? Well, it would be, if Analytics had an API :(

Fortunately, there are geniuses out there who have done genius things. In this case, Chris Riley of Blogoscoped has mashed up a combination of Google and Yahoo! offerings in such a way that we poor Blogger users can have a Popular Posts widget!

The basics

The basics are these:
1) Have analytics setup on your blog
2) Setup a Google Group
3) Setup a GMail account
2) Setup email notifications from Analytics' top pages to a GMail account
3) Setup GMail forwarding to your Google Group
5) Setup a Yahoo! pipe to fetch and process your top content report
6) Setup a pseudo-widget script on your blog to display the pipe's results

The pipe

While Chris provides his Yahoo! Pipe for others to clone, I had issues getting it to work with fetching the page's title contents. Here's the pipe that I'm using. Of course, a different pipe means different code for parsing the results.

The javascript

Here's the javascript that I use to display my pseudo-widget:
<div id="popularposts"><noscript>Please enable javascript to view the most popular posts list.</noscript></div> <script type="text/javascript"> <!-- // Robust pipe-output-handling function // as found at http://techknack.blogspot.com/2008/01/most-popular-posts-list-for-blogger.html function topcontentCallback(obj) { try { var url,title,output; output='<ul>'; for (i=0; i< obj.count; i++) { url = obj.value.items[i].url; try { title = obj.value.items[i].name[0].content; title = title.replace('<title>', ''); title = title.replace('</title>', ''); title = title.replace('Tech Knack: ', ''); } // if we didn't get the title (bad Pipe!), give a pseudo- // title from the URL catch (e) { title = url.substring(url.lastIndexOf("/")+1, url.lastIndexOf(".html")) .replace(/\-/g, " "); } //remove the <title> tags the pipe leaves in. output+= '<li><a href="'+url+ '" title="'+title+'">'+title+'</a></li>'; } output += '</ul>'; document.getElementById("popularposts").innerHTML = output; } catch (e) { var error = "Error fetching data: "+e; if (e.toString().indexOf('i')!=-1) error += "<br />i: "+i; document.getElementById("popularposts").innerHTML=error; } } //--> </script> <script src="http://pipes.yahoo.com/pipes/pipe.run?_id=bi6F5qPK3BGfvuxf1vC6Jw&_render=json&_callback=topcontentCallback" type="text/javascript"> </script>

Yes, my JS is admittedly a bit more robust than Chris's. This is mostly because of various issues I either ran into or thought of while trying to implement the widget. For those who are interested, I'll post an explanation of the code in another post.

For this to work, you must enable "post pages" in your template's preferences. There might be a way to get this to work without post pages, but I wouldn't recommend it ;)

If you are going to copy and paste this code (probably after cloning my pipe), be sure to change things appropriately:

Change the pipe ID in the last script element to the ID of your new pipe

In the "title fixup" section:
try { title = obj.value.items[i].name[0].content; title = title.replace('<title>', ''); title = title.replace('</title>', ''); title = title.replace('Tech Knack: ', ''); } The last "title.replace" line removes the site-wide title that is used on every page. Change it according to your Blogger setup (if you leave it as-is, though, it shouldn't hurt anything).

Additional links:
No Google Analytics API? No Problem!
Yahoo! Pipes

January 23, 2008

Time for a redesign!

Add this post to Del.icio.us. Del.icio.us (0 saved)

I chose the dark design for this blog from the default blogger themes...a looong time ago (or so it seems)! Since then, I've taken a usability course in school, read many things on the web concerning usability, and been active at Deviant Art.

I believe it is time for a fresh look :D
The testing grounds: techknack2.blogspot.com ;)

One of the things I've heard recently is that white text on dark backgrounds is not all that usable. Cool, but not usable. As I've gone back and read a couple of my posts, I've realized that it's true, especially for long blocks of text; the text just seems to fade into the background after a rather short amount of time.

Another issue I've had is clutter; the more I look at my blog, the more clutter I see. Whitespace is good; simpler is better. Keep it simple, stupid.

In my search for templates for the new Blogger, I've found but one that I really like: Azul by karan at Skins4Bogger. Unfortunately, the XML download is for a completely different theme...

As per usual, I appreciate any and all feedback. What do you expect to see in a blog's layout? Where do you expect to see these things? Should the comment count be before the post, or after? Etc :)