Scott Hanselman

Installing and Running node.js applications within IIS on Windows - Are you mad?

August 28, '11 Comments [59] Posted in IIS | nodejs | Open Source
Sponsored By

iisnode registering node.js in IIS management Some folks on our team have been working on making node.js work awesomely on Windows. There's a few questions you might have.

First, what's node.js?

If you're not familiar with node.js, it's a new web programming toolkit that everyone's talking about. It's the one that makes you feel not hip if you don't know what it is. Like Ruby on Rails was a few years back. Folks called it "Node" and it's basically server-side JavaScript. The idea is that if you are doing a bunch of JavaScript on the client and you do JavaScript all day, why not do some JavaScript on the server also. One less thing to learn, I suppose.

If you are an ASP.NET programmer, you can think of node.js as being like an IHttpHandler written in JavaScript. For now, it's pretty low-level. It's NOT an HttpHandler, but I'm using an analogy here, OK? Here's a lovely article by Brett McLaughlin that goes into more detail about Node.js and what it is. His subtitle is "Node isn't always the solution, but it does solve some important problems" and that's just exactly it.

UPDATE 1: Why does node.js matter?

Why bother with node at all? There's a number of interesting aspects to node as it sits. It uses a very fast JavaScript engine called V8, but more importantly its I/O is asynchronous and event-driven which contrasts with typical synchronous code.

For example, a naive hello world HttpHandler in ASP.NET that "does some work" for a few seconds (gets a file, accesses a service, etc) could look something like this:

public class SimpleHandler : IHttpHandler 
{
public void ProcessRequest(HttpContext context)
{
Thread.Sleep(2000); //Do something that takes a while
context.Response.Write("Hello from SimpleHandler");
}

public bool IsReusable { get { return true; } }
}

And this is usually fine for most stuff. However, when I push this HARD with a load testing tool and a thousand virtual clients, I can barely get 60 requests a second. The request thread is tied up waiting for the "work" to happen and everyone else gets in line. I'm using up ASP.NET pool. It'd be nice if the work would get handled and someone would "call me back" when it's finished. It's like waiting on hold for tech support. You are effectively blocked as you wait for them to pick up their end. Wouldn't it be nice if they just called you back when they were ready?

ASP.NET has always been able to do things (see this MSDN article from 2003 on Async Handlers) with IHttpAsyncHandler but it's always been a bit hard and almost no one knows about it. With the Async CTP and the Task libraries built into .NET, you can build a nicer abstraction on top of IHttpAsyncHandler. Ayende has a simple example AbstractAsyncHandler (there's many of these out there, including a few in our own tools, some things in MVC, and some things in SignalR (more on that soon)) that we can use to do similar work. This example could also do other more complex and pertinent things like file IO, db IO or calling a web service. This is a naive example that doesn't map exactly to the node one below, but it makes the point. Plus, it's nice to look at.

public class SimpleAsyncAyendeHandler : AbstractAsyncHandler
{
protected override async Task ProcessRequestAsync(HttpContext context)
{
await TaskEx.Delay(2000);
await context.Response.Output.WriteAsync("Hello from Ayende and Scott");
}
}

Pointing the same 1000 virtual clients at this handler gives me 500 requests a second, which makes sense as a request takes 2 seconds to finish. If we were doing I/O or other more complex and long running things than waiting, this scales better than the first example. Doing asynchronous code in .NET as well as parallelism is much easier than before, as evidenced by the two lines of  code above and the simplicity of Ayende's small example. The fact that this kind of thing is easy and elegant in node is an attractive thing about node.

Node loves asynchrony, and uses JavaScript callbacks to provide asynchrony in a pleasant way. You use events and callbacks in JavaScript already on the client, why not use them on the server? Here's an example from Marc Fasel's blog on the topic.

First, some synchronous file work via Marc:

var fs = require('fs'), filenames, i, processId;

filenames = fs.readdirSync(".");
for (i = 0; i < filenames.length; i++) {
console.log(filenames[i]);
}
console.log("Ready.");

processId = process.getuid();

And the same work using an asynchronous pattern that may look familiar!

var fs = require('fs'), processId;

fs.readdir(".", function (err, filenames) {
var i;
for (i = 0; i < filenames.length; i++) {
console.log(filenames[i]);
}
console.log("Ready.");
});

processId = process.getuid();

The I/O happens and the callback function that's dependant on the result is executed when the I/O is finished. Powerful stuff.

Why would I want node.js to run on Windows and IIS?

Tomasz Janczuk is working on the iisnode project lately. You might think that Windows and node don't belong together. "That's just wrong! What are they thinking? I thought IIS was all about .NET?" Well, you may recall I spoke at CodeMash a few years back on IIS7 and PHP and did a screencast showing how IIS7, PHP and FastCGI could push many thousands of requests a second. The IIS folks, the Windows folks, the Azure folks, want to make sure everything runs well on Windows. Remember, we sell Windows, so it's good if it does many things well. ;)

Why bother getting node to run on IIS? Tomasz says it best:

Some of the advantages of hosting node.js applications in IIS using the iisnode module as opposed to self-hosting node.exe processes include:

  • Process management. The iisnode module takes care of lifetime management of node.exe processes making it simple to improve overall reliability. You don’t have to implement infrastructure to start, stop, and monitor the processes.
  • Scalability on multi-core servers. Since node.exe is a single threaded process, it only scales to one CPU core. The iisnode module allows creation of multiple node.exe processes per application and load balances the HTTP traffic between them, therefore enabling full utilization of a server’s CPU capacity without requiring additional infrastructure code from an application developer.
  • Auto-update. The iisnode module ensures that whenever the node.js application is updated (i.e. the script file has changed), the node.exe processes are recycled. Ongoing requests are allowed to gracefully finish execution using the old version of the application, while all new requests are dispatched to the new version of the app.
  • Access to logs over HTTP. The iisnode module provides access the output of the node.exe process (e.g. generated by console.log calls) via HTTP. This facility is key in helping you debug node.js applications deployed to remote servers.
  • Side by side with other content types. The iisnode module integrates with IIS in a way that allows a single web site to contain a variety of content types. For example, a single site can contain a node.js application, static HTML and JavaScript files, PHP applications, and ASP.NET applications. This enables choosing the best tools for the job at hand as well progressive migration of existing applications.
  • Minimal changes to node.js application code. The iisnode module enables hosting of existing HTTP node.js applications with very minimal changes. Typically all that is required is to change the listed address of the HTTP server to one provided by the iisnode module via the process.env.PORT environment variable.
  • Integrated management experience. The issnode module is fully integrated with IIS configuration system and uses the same tools and mechanism as other IIS components for configuration and maintenance.

    In addition to benefits specific to the iisnode module, hosting node.js applications in IIS allows the developer to benefit from a range of IIS features, among them:

    • port sharing (hosting multiple HTTP applications over port 80)
    • security (HTTPS, authentication and authorization)
    • URL rewriting
    • compression
    • caching
    • logging

These are all compelling, but the most interesting bit here, in my opinion, is integration. The iisnode module is a proper IIS module, just like ASP.NET and PHP. This means you can have a single website that has multiple kinds of content. Restated from above:

For example, a single site can contain a node.js application, static HTML and JavaScript files, PHP applications, and ASP.NET applications.

Sometimes folks freak out when I say you can have an ASP.NET WebForms app and a ASP.NET MVC app in the same AppPool as a "hybrid." Frankly, Dear Reader, people don't even realize the power and flexibility of IIS. When you plug in something new like node but run it the way you run other things it inherits all the coolness of the outer container, in this case, IIS.

Fine, you got me, how do I run node.js on Windows?

I'm assuming you are running IIS7.

  • Go download node.exe, and put it in c:\node
  • Go download a build of iisnode.
  • Unzip iisnode's zip into \inetpub\iisnode
    • (that was my idea, not sure if it's the best place)
  • From an Administrator Command Line, run install.bat.

The install.bat will:

  • unregister existing "iisnode" global module from your installation of IIS if such registration exists
  • register iisnode as a native module with your installation of IIS
  • install configuration schema for the "iisnode" module
  • remove existing "iisnode" section from system.webServer section group in applicationHost.config
  • add the "iisnode" section within the system.webServer section group in applicationHost.config
  • delete the iisnode web application if it exists
  • add a new site iisnode to IIS

No warranties! Be careful, you're living on the edge. Remember, you're reading this stuff on some random dude's blog.

WARNING: I couldn't figure out the right permissions for the AppPool and the File System so I wimped out and gave my local AppPool "SYSTEM" permissions. This is awful and totally my fault. I filed an issue on the iisnode GitHub and I'll fix it and update this post when I hear back.

I made a new AppPool just for node, gave it SYSTEM access, then assigned the Node Site to this new AppPool. Your site should look like:

Node Site in IIS7

And if you click on Modules for this Site in IIS7 you should see iisnode as a native module:

Hey, it's iisnode as a native module, just like I said. Crazy.

At this point, you should be able to hit http://localhost/node/helloworld/hello.js and get back:

Hello, world! [helloworld sample]

The contents of which are simply:

var http = require('http');

http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, world! [helloworld sample]');
}).listen(process.env.PORT);

Lovely.

Fooling around with WCAT (Web Capacity Analysis Tool) and node.

Disclaimer: To be clear, this is so very fooling around. This is just to show that it works and it can do the basics really fast. I'm not doing a benchmark nor am I saying "this works better than this other thing." Remember, they just got started recently porting node itself to Windows, and Tomasz and friends are just beginning their work. So, don't overreach. That said, the preliminary work they are doing is really impressive.

I couldn't help myself. I mean, it's one thing to install a helloworld of some new thing, run it once and go "OK, that runs." It's another to pound it until it says "Uncle." After I got the hello world stuff working, I wanted to do some poor man's stress testing to see what the players involved did.

First, I installed WCAT, a free Web Capacity Analysis Tool from the IIS team.

  1. WCAT 6.3 x86
  2. WCAT 6.3 x64

Warning. This is a command-line only tool and it's really persnickety when you run it. It's confusing and it took me a minute to setup.  Here's the steps I took after installing. This is all from an Administrator Command Prompt. Note also that I'm doing this all on one machine, which is cheesy, but remember, it is a GOM.

  1. cscript //H:Cscript
  2. wcat.wsf –terminate –update –clients localhost
  3. Then I made a folder I called \nodetests and I put these three files in it:

wcat.bat

pushd C:\Users\Scott\Desktop\nodetests
"c:\program files\wcat\wcat.wsf" -terminate -run -clients localhost -f settings.ubr -t nodescenario.ubr -s localhost -singleip -o C:\Users\Scott\Desktop\nodetests
pause

nodescenario.ubr (or call it whatever you want)

This is so basic. It just beats on the four sample applications for a while.

scenario
{
name = "node_fun";

warmup = 30;
duration = 90;
cooldown = 30;

default
{
setheader
{
name = "Connection";
value = "keep-alive";
}
setheader
{
name = "Host";
value = server();
}
version = HTTP11;
statuscode = 200;
close = ka;
}

transaction
{
id = "foo";
weight = 1000;
request
{
url = "/node/logging/hello.js";
}
}
transaction
{
id = "bar";
weight = 2000;
request
{
url = "/node/helloworld/hello.js";
}
}
transaction
{
id = "baz";
weight = 2000;
request
{
url = "/node/defaultdocument/";
}
}
transaction
{
id = "bat";
weight = 2000;
request
{
url = "/node/configuration/hello.js";
}
}
}

settings.ubr

I just copied in the one from samples and uncommented out and changed (and tweaked during testing) these lines:

server         = "hexpower7";
clients = 1;
virtualclients = 8;

Now, run the Test

Next, I ran wcat.bat as an Administrator...you can see all the little node.exe's firing up. I've got a

(Remember they are running as SYSTEM because I was unable to figure out the right permissions. That's my bad, no one else's. I'll figure it out one day.)

Lots of little node processes

Here's the WCAT tool's console output...I'm able to consistently do 10,000 hello worlds a second and ended up with just under a million normal requests and responses in 90 seconds. That's a lot of hello worlds.

Remember Hanselman's Rule of Scale.

"If you do nothing, you can scale infinitely." - Me

Of course, this is all local on a fast machine. This is just hello world (with some logging) so it's not testing node much, nor IIS much, but rather the collaboration between the whole system, IIS, iisnode, and node itself.

Aside: an ASP.NET IHttpHandler doing the exact same thing on this same machine gets 22,500 requests a second, so node and iisnode has some room to improve, which is great.

Here's the node/iisnode results:

Pushing a million transactions in 90 seconds

There's a lot of things I could configure on both sites, number of clients, virtual clients, as well as iisnode-specific settings (which are, nicely enough, managed in a web.config:

<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
</handlers>
<iisnode
nodeProcessCommandLine="%systemdrive%\node\node.exe"
maxProcessCountPerApplication="4"
maxConcurrentRequestsPerProcess="1024"
maxPendingRequestsPerApplication="1024"
maxNamedPipeConnectionRetry="3"
namedPipeConnectionRetryDelay="2000"
asyncCompletionThreadCount="4"
initialRequestBufferSize="4096"
maxRequestBufferSize="65536"
uncFileChangesPollingInterval="5000"
gracefulShutdownTimeout="60000"
loggingEnabled="true"
logDirectoryNameSuffix="logs"
maxLogFileSizeInKB="128"
appendToExistingLog="false"
/>
</system.webServer>
</configuration>

This is pretty cool stuff. I like that the team I work with is excited to make things work well on IIS and I'm stoked that I can mess around with node now without firing up VMs. I'll report back as I learn more!

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

NerdDinner being updated to MVC3 with Razor, HTML5, GeoLocation, EF CodeFirst, jQuery Mobile, YepNope and Modernizr and a fixed Mobile Device Capabilities ViewEngine

August 26, '11 Comments [28] Posted in ASP.NET | ASP.NET MVC | HTML5 | IE9 | Mobile | NerdDinner
Sponsored By

NerdDinner with jQuery Mobile on an iPhone asking for my locationTwo years ago Rob, Phil, and I released our MVC 1.0 book with the Gu helping with the big intro. I created the basic Nerd Dinner sample application (code here) and released the first 185 pages for free. Since the initial 1.0 release, we've had help from lots of people like Dave Ward, Andrew Aarnott and Jon Galloway on a lot of little things like JavaScript and OpenAuth support, then John V. Petersen moving us to MVC3 with Razor Views, and Peter Mourfield taking on lots of JavaScript and new features. These guys started working on a feature here and a feature there in our spare time. It's been a collaborative effort with folks dropping in, contributing here and there with long periods of silence in between.

After John V. Petersen moved us to MVC3 and Razor, Peter Mourfield got on a feature tear and now we've added or updated:

Now, to be clear, this isn't a release, it's an initial almost release. Call it an Alpha. That said, it's up live at http://www.nerddinner.com and the source continues to move forward in the Trunk on Codeplex. We've got some inconsistencies with the mobile site and back  button, and geoloc is not working completely on an iPhone but it's decent on a browser. We're working on this an hour here and an hour there, but if you, Dear Reader, find any bugs in the trunk or obvious stupid mistakes, please to let us know and *cough* submit a patch *cough*. Or at least leave a nice Issue we can track.

As we get these features working rock solid, Pete and I will do a series of posts digging in to what worked well and what didn't in each feature. Already Pete has a good blog post talking about adding HTML5 Geolocation to Nerd Dinner with yepnope.js, and Modernizr. He used yepnope, a great library for saying "do you support this feature? Yep? Get this JS. Nope? Get this JS. For example:

<script type="text/javascript">
$(document).ready(function () {
yepnope({
test: Modernizr.geolocation,
yep: '@Url.Content("~/Scripts/geo.js")',
nope: '@Url.Content("~/Scripts/geo-polyfill.js")',
callback: function (url, result, key) {
getCurrentLocation();
}
});
});
</script>

Love it. More details and code on Pete's blog post. In the image below you can see IE9 warning me that my local site wants to track my physical location.

Geolocation in IE9

Here's Firefox prompting me for a location on the live site:

Firefox wants to know where I am

And Chrome:

Chrome knows where you live!

As I said, there are surely lots of bugs and subtleties we need to iron out, but I'm glad we're actually moving this sample forward. Hope you enjoy, and do feel free to fix our bugs for us, Dear Reader. ;)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast Episode Rollup 273 through 280 - Glimpse, JavaScript, Kinect, Script#, PolyGlot, Azure, Windows and Graph Databases

August 25, '11 Comments [0] Posted in Podcast
Sponsored By

It's happened again, I've gotten behind on my Podcast posts and rather than flooding you with copy-pastes, I figured I'd do a rollup post. It's a shame because, if I may say so, I think it's been a great few months for the show. Perhaps I'll start doing rollups every four episodes.

If you haven't listen to the show before, or perhaps listened in the past and stopped, now is a great time to get back on board.  Also, please do take a moment and review the show on iTunes. The show is limited to about 30 minutes long, so it's perfect for your work commute.

You can subscribe easily in a number of ways:

Subscribe: Subscribe to Hanselminutes or Subscribe to my Podcast in iTunes or Zune

Here's what's been going on with the last few episodes:

Haixun Wang #280 Microsoft Research: Trinity is a Graph Database and a Distributed Parallel Platform for Graph Data

Scott talks via Skype to Haixun Wang at Microsoft Research Asia about Trinity: a distributed graph database and computing platform. What is a GraphDB? How is it different from a traditional Relational DB, a Document DB or even just a naive in-memory distributed data structure? Will your next database be a graph database?


Within Windows#279 Within Windows with Rafael Rivera

Scott sits down with Rafael Rivera to talk about the black box that is Windows. Or is it? Rafael doesn't take no for an answer and shares stories of breaking apps to fix them. No more secrets, this week on Hanselminutes.


Windows Azure Logo#278 Microsoft Web Platform and Azure direction with Scott Hunter

Scott Hanselman and Scott Hunter (also known as Scotts the Lesser) talk about recently Azure/Web reorg, the direction that ASP.NET and Azure are talking, and how they see open source fitting into the future at Microsoft.


#277 Polyglot Programming and .NET - Lessons Learned with Ivan Towlson from Mindscape

Mindscape LogoScott sits down with Ivan Towlson from Mindscape. They recently released Web Workbench to the community for free with support for LESS, SASS, and CoffeeScript. Interestingly, they used C#, F#, JavaScript and Ruby to create this app. Why was polyglot programming right for what them? Is it right for you?


Earth Class Mail Logo#276 Script# compiles to JavaScript: A Real World Implementation at Earth Class Mail

Scott talks to Matt Clay and Matt Davis at Earth Class Mail about how they used Nikhil Kothari's Script# compiler to write JavaScript from C# source. Why did they do it? What were the benefits? The problems? Would they do it again?


Kinect#275 Digging into the Kinect SDK with Dan Fernandez

Scott gets schooled on the Microsoft Research Kinect SDK by Dan Fernandez. What happens when I plug a Kinect into my PC? What's included with the SDK and what's not? What work happens in the hardware and what happens in software...and more importantly, what can I build?


Semantic Markup#274  JavaScript is Assembly Language for the Web: Semantic Markup is Dead! Clean vs. Machine-coded HTML

Scott talks to Erik Meijer about the idea that JavaScript is an assembly language. What assumptions can we make and how could this idea fundamentally change how we develop software on the web?


Glimpse#273 Glimpse - A client-side Glimpse into your server

Scott talks with open source developers Anthony van der Hoorn and Nik Molnar from the Glimpse Project. Their very innovative (and all JavaScript and HTML!) debugger tool for ASP.NET has taken the community by storm. How did they do it and how can Glimpse make your live better?


Also, don't forget that Telerik is our sponsor and we love them.

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Forms and WPF. Enjoy developer tools like.NET Reporting, ORM, Automated Testing Tools, Agile Project Management Tools, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visit www.telerik.com.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Learning about Progressive Enhancement - Supporting Mobile Browsers with CSS3 Media Queries

August 25, '11 Comments [32] Posted in ASP.NET | ASP.NET MVC | Javascript | Mobile
Sponsored By

This site on an iPhone 4This site on a Windows Phone 7 MangoI blogged about how happy I've been working with designer Jeremy Kratz on my recent site redesign. We've been meeting lately about small improvements and tweaks. One of the core goals was to support many screen sizes from 30" down to a small smart phone screen. You can see the results on the right in a Windows Phone and iPhone.

My team is doing a lot of work on Mobile lately, not just with things like jQuery Mobile and what that means not just for ASP.NET but the mobile web in general. We've also worked with 51Degrees and they've even created a NuGet package that can help you tailor your site to any device. You'll hear more about Mobile in ASP.NET from Steve soon and I'll be sharing some ideas in a few upcoming conferences.

I originally enabled this blog for mobile browsing this same week in 2006. That's 5 years ago, when mobile was really just getting started. Back then, I had a separate mobile view that was stripped down and emitted totally different markup based on user-agent sniffing. This was pretty innovative for the time, a half decade ago. There's a number of things in ASP.NET 2 (what this blog runs on) that do adaptive markup. However, more modern techniques use a combination of detecting capabilities on the server side with feature detection on the client. We query the browser with JavaScript: Do you support Geo-Location natively? No? How about using this shim JavaScript library?

When laying out a page, rather than serving up different markup on the server based on the server-detected browser, we can also use CSS3 media queries that modify a layout based on screen (viewport) size. I wanted to use these techniques for this blog.

While I realize that some people want a totally custom iPhone web application when they visit a site, I draw a specific line between a Web Site, a Mobile Web Site and a Mobile Web Browser Application. When visiting what I consider a regular web site, I don't want an iOS-specific Web version and a Regular Web I can't see. What I'd like to see is the site I went to, perhaps with alternative elements or a few things moved around.

However, if the site is full of interactivity, I might want a Mobile Web Site served up with different - perhaps device-specific - markup for different Smart Phones. I think the decision point for how application-like, and therefore device-specific, your mobile website should be hinges on how much your user will be interacting with it vs. just reading/consuming content.

With this blog, there's not a lot that needs to be smart phone specific. I want a mobile-optimized version of the site, not a complete iOS (or Android, or WP7  or whatever) re-imagination of the site. If I did, I'd download an app.

If you're creating a Web Application that focuses on "producing" (meaning pushing buttons, doing application stuff) I can see using a phone-specific framework, but when "consuming" content, I'd like my Web Site to look like a Web Site.

CSS3 Media Queries

Designer Jeremy and I (mostly him!) came up with a goal to support desktops, tablets and netbooks around 1024 pixels wide, and phones less than or around 720 pixels wide.

Here's the general structure of the CSS. Note that none of this is JavaScript. This is all just descriptive "when the screen is like this, apply these styles" stuff.

/* psuedo CSS, not complete */

/* CSS Reset from http://meyerweb.com/eric/tools/css/reset/

Goes here
*/

/* Global Styles for the Desktop Site */


/* Force the images to resize smaller
when they get squished with small screens */
img
{
width: auto !important;
height: auto !important;
max-width: 100%;
}

/* bunch of styles */

/* Hide the dropdown we'll use for
navigation when we are tiny */
#nav select
{
display: none;
}

/* Tablet device styles */

@media screen and (max-width:1024px)
{
/* hide stuff, make things smaller, yank the right rail */
#sidebar
{
display: none;
}
/* hide other stuff */
}


/* Phone device styles */

@media screen and (max-width:720px)
{
/* hide the huge top nav and show the nav dropdown */
}

Desktop vs. Mobile Site Navigation

Everything looks nice, until you resize the site and the top navigation bar really got squished, then overflowed. However, Jeremy had a great idea. The navigation at the top is nice, but when the screen is REALLY small the navigation is overwhelming. The navigation is just an unordered list like this:

<div id="nav">
<ul id="primarynav">
<li id="nav-info"><strong>Info</strong>
<ul>
<li><a title="Learn more about Scott Hanselman" href="http://www.hanselman.com/blog/AboutMe.aspx">About Me</a></li>
<li><a title="Contact Scott Hanselman" href="http://www.hanselman.com/blog/contact.html">Contact Me</a></li>
</ul>
</li>
<ul>
...more..
</div>

Why not show that as a dropdown for tiny screens? There's actually a StackOverflow question on that (did you know Jeremy did the initial StackOverflow design?) I could certainly just put a dropdown in a DIV and hard code it, but since the structure already exists in a UL and I might want to change it in the future as well as avoid duplication, we'll use JavaScript to inject the SELECT into the HTML DOM (Document Object Model).

Basically we just spin through the UL structure and create the select, options and (bonus!) option group using the existing hierarchy.

$(document).ready(function() {
var markUp = ["<select id='primarynav-select'>"], $li, $a;
markUp.push("<option value='' selected='selected'>Go to...</option>");
$("#primarynav > li").each(function(){
$li = $(this);
if($li.find("li").length){
markUp.push("<optgroup label='"+$li.find("strong").text()+"'>");
$li.find("li").each(function(){
$a = $(this).find("a");
markUp.push("<option value='"+$a.attr("href")+"'>"+$a.text()+"</option>")
});
markUp.push("</optgroup>");
}
else{
$a = $li.find("a");
markUp.push("<option value='"+$a.attr("href")+"'>"+$a.text()+"</option>")
}
});
markUp.push("</select>");

$("#primarynav").after(markUp.join(''));
$("#primarynav-select").change(function(){ window.location = $(this).val(); });
});

So this,

The navigation menu as a UL

becomes this, when you resize the browser really small, just by hiding the one and showing the other using the CSS above.

The UL Navigation menu as a Select Dropdown box

Resizing Images for Small Screens

For an important site that gets a lot of mobile traffic, you would want to use a browser database like 51Degrees that could tell you the size of the screen of a specific phone and the graphics formats (PNG, JPG, transparency or not) so you could resize large images on the server side and return mobile-optimized images. You could have a handler that detects small phones with small screens then resizes and caches images. Perhaps even grayscales them for non-color phones.

In my case, my images aren't that large and it's not that important to me to do server-side resizing. Instead we decided to have the images resized on the client side using CSS like this:

img
{
width: auto !important;
height: auto !important;
max-width: 100%;
}

This says, no matter what other CSS or the markup says, it's important that images resize to 100% but no larger. This was a nice simple solution for those larger photos that I put at the top of each post. Rather than overflowing and clipping against the edge of the phone, then resize. You can see this in action by just resizing your browser.

Here you can see this site on an IPad. Note the right rail has disappeared. On the right, the same post on an iPhone 4 which is 640px wide. See how the image has moved up and is the width of the phone?

This site on an iPad  This site on an iPhone 

You can even use CSS Media Queries, without any JavaScript, to detect iPad orientation, or if the user has an iPhone 4 with high-res retina display vs. a low-res iPhone 3G.

Here's example CSS from Web Designer Wall, credited to Thomas Maier and Cloud Four respectively.

<!-- Detect iPhone4 Retina Display from CSS -->
<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" type="text/css" href="iphone4.css" />

<!-- Detect iPad and iPad orientation -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">

You can actually test and simulator the orientations locally by making your desktop browser wider than it is tall.

There's still a few things left to tweak, and these techniques only work with modern Smart Phone browsers that support these features, but for the 8% of you who are reading this blog on a mobile device it should be a better experience now.

One Caveat - Code Samples

As with all nice things, there is one bad thing. The code samples that I have use SyntaxHighlighter. It takes a <pre> and using JavaScript does a lot of work on them, effectively replacing the PRE in the DOM with a bunch of stuff. This looks lovely in a desktop browser and crappy on mobile. I'm still exploring how to fix this. Here's some ideas I have.

  • Conditionally override the SyntaxHlighter CSS using Media Queries
  • Change the plugin to keep the <pre> around, but hidden. Then swap the highlighted one with the plain one on phones
  • Come up with better CSS all up for the highlighter

I'm interested in your thoughts!

UPDATE: I'm currently reading Adaptive Web Design: Crafting Rich Experiences with Progressive Enhancement as it's amazing. Aaron gave me a review copy to check out. He outlines a similar UL to SELECT enhancement and a dozen others in a clear, clean readable manner. I'm enjoy it thoroughly.

Related LInks

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

The Technical Friend's Essential Maintenance Checklist for Non-Technical Friend's Windows Computer

August 24, '11 Comments [77] Posted in Musings | Tools
Sponsored By

World's Dustiest Computer I visited a friend today and noticed their computer. Of course I did, that's what we do. It was, as are all non-technical computer friends' computers, a 3 year old gray Dell mini tower with a 17" flat screen. In fact, these are the ONLY computers that non-technical computer friends own, you know.

I was there to visit, but I'm genetically incapable of NOT doing a system's check on an old PC. "How can these people LIVE like this?"

If you are reading this blog, you have been in this very situation. Perhaps your wife is waiting in the car to go home even after a lovely dinner with your couple friends and you'll be there in "5 minutes" but you're actually hypnotized by the defragmenter, trying to influence the hard disk head's pixel indicator with your mind. Which sector is next...damn! Immovable!

It's a dance, what we do to our friend's computers, isn't it. It's a dance we done a thousand times, but one that is comforting in its steps. Not surprised, nothing unknown, just maintenance. We've said one day we'll write a script, one day we'll create the ultimate batch file to do all this, but until then, click, download, install, update, rinse, repeat.

Hardware

  • RAM RAM RAM - I've got sticks of the stuff. Today my neighbor had a 512meg machine that was just sad to look at. I've said before, a machine with too little RAM is a three legged dog. You know he's gonna get where he's going but it's hard to watch. I had a couple of gigs of PC-5300 in the car for some reason and I just added it and got them up to 2gigs. Minds were blown.
  • WEI: Window Experience Index - I've gotten into the habit of pressing 'WindowsKey+PauseBreak' on any new Windows Machine to take a look at the Windows Experience Index. It's the number that will tell you, reasonably and quickly, how much your friend's old computer sucks. The top score is currently a 7.9. Usually these machines are 3s and 4s. The important part is the the score is the lowest scoring component. The best part is that most of these low scoring components are cheap. $50 for some RAM, $50 for a new  HD, $50 for a decent Video Card. Update the crappiest part. Again, chances are you have better hardware lying around in your junk drawer than does Non-Technical Friend.

Software

  • Update Drivers - Non-Technical Friend always has a piece of hardware that doesn't work correctly, usually a printer, sometimes an All-In-One scanner/printer/fax. Almost always getting the right drivers solves it. Help them out.
  • Windows Update AND Microsoft Update - They usually haven't set Windows Update to automatically install updates at 3am, and they never have clicked "Yes" to get updates for other Microsoft products. (You have to opt-in.) Visit http://www.microsoftupdate.com on machines before Windows 7 and run the Windows Update applet on Windows 7 and setup updates for Microsoft products (Office, etc. as well as Windows.) Make sure they've got the latest Service Packs for Windows and Office.
  • Anti-Virus - When I visited my friend today I was surprised to see that they had Anti-Virus and Anti-Malware software installed. Always a refreshing surprise. However, they had several installed, and they were fighting. They had AVG, Windows Defender, PC Optimizer, SpyBot, and two other playfully named "anti-spyware" applications as well as four browsers toolbars. I uninstalled the whole lot and installed Microsoft Security Essentials. It's simple, lightweight, solid and free. It's also free on up to 10 PCs for Businesses.
  • Uninstall anything evil - If you want to get a quick look at what's on a machine and uninstall LOTS quickly, look no further than NirSoft's My Uninstaller (download). Remove Toolbars (they think they need them and they never do and won't miss them), and anything that looks like it might destabilize their system. I check out toolbars, add-ins, etc.
  • Update Stuff that can't Update Itself - I usually install FoxIt Reader or at least update Adobe Acrobat and Flash. I updated their Firefox 3.6 (!) to Firefox 6 and installed IE9.
  • Backups or at least DropBox - I always ask "Is your stuff backed up" and they always say "I have been meaning too" or "On my thumb drive last month." I should start charging folks to install DropBox. In this case, I thought about Mozy but my friend really just needed some documents backed up so now they've got DropBox with 2GB free. Windows Live Mesh is another decent option if they are Hotmail/Live Messenger users. Regardless, for goodness sake, get their data to the cloud!

Teaching

  • Tell them about Security - Wanna freak them out? Ask your friend for the last four digits of their social security number (or national id) then hit the Start Menu and search for it. You'll almost always find Excel files, Word documents or PDFs with their super-private information. I've often found scans of their actual id cards. This trick also works with their credit cards. Teach your friends to not keep personal information on their machines.
  • Teach them about History - Avoid looking at Non-Technical Friend's browser history or searching Google for anything that starts with "a", "p" or "s." You don't need to know Non-Technical Friend that well. Perhaps come up with an excuse to teach them about Private Browsing or "Incognito" if you're cool like that.
  • Teach them about Passwords -  Explain how passwords work to your friends. Suggest they use pass phrases, add complex characters, stop saving their passwords in the browsers. If they are cool with it, clear their passwords in their browsers and suggest a password manager.

Maintenance

  • Cleaning - Explain to them the importance of cleaning (or burning) their 10 year old keyboard. Computer keyboards are dirtier than toilets. That's a fact I just made up and I'm totally sure it's true. Get that biomatter off your keyboard and mouse. You wouldn't lick your keyboard but you'll type it on for hours then touch your face. You nasty.
  • Blow the Dust out of their System - It's just the right thing to do. Seriously, keep a bunch of Cans of Air Dusters in your car. I do. Get out the vacuum at your friends house, give them proper warning that it's gonna get NASTY up in here and open up their computer. Chances are this is the first time it's ever been open. It's probably got that "there's a weird sound in the back" groan that's indicative of dust in the power supply. I can't tell you how many times I've just jammed a pencil in there just to get a computer to SHUT UP.
  • Defrag - Of course Windows has its own defragmentation program but I love the MUCH better Auslogics Disk Defrag
  • Startup Programs - Get AutoRuns (or RegEdit to HKLM\Software\Windows\CurrentVersion\Run) and clean up all the random stuff that's starting up but they've forgotten why. If they are slightly technical, suggest Soluto for fixing their startup issues.
  • Tidy Up - Non-Technical Friend typically has some toolbar - perhaps an Explorer Folder or something - pinned off to the far left side of their screen and isn't sure how it got there or how to get rid of it. Clean up these things.
  • Crap Cleaner - It used to be called Crap Cleaner but now it's tamely known as CCleaner and it's the only "cleaner" I trust. I also run Disk Cleanup (as Administrator) that comes with Windows.
  • Resolution - Non-Technical Friend always has a 17" LCD with a native resolution of 1280x1024 but runs their system at 1024x768 and never understands why everything looks blurry. Save them.
  • Join.me - Show them how Join.me works and explain that their Technical-Friend-Who-Isn't-You can help them in the future remotely to tidy up and do routine bits and bobs without needing to come over.

Finally, give them a hug and tell them it's OK. What are your favorite maintenance tasks while visiting Non-Technical Friend, Dear Reader?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.