Wednesday, 23 December 2009

hasLayout problems after all these years

Having terrible trouble with all version of IE trying to get some peekaboo text to stay put. In this scenario a div was not paying ball at all, after setting hasLayout on pretty much every element in the entire page I realised that the system was outputting HTML without a DTD, dogh, I feel like i've just called tech support to complain about not being able to print, only to find out that the cleaner unplugged the printer.

Note to self, always check the most obvious thing first!

In IE7 I have a normal input type = button which as the behaviour that when you scroll the text in the button gets visually corrupted. So very slowly scrolling the button into view will wipe the text and the button will appear empty. The offending button is fine in IE6 and IE8, I was tempted to ignore this and mark it down as a bug in the browser but weakened and decided to fix it.

After tinkering with the CSS for a while I couldn't find any hack/fix for this issue, setting the writing-mode style to top to bottom, did fix it but didn't think having the button text written out vertically would go down well!

In the end....


window.attachEvent ('onscroll', function(){
// layout fix for IE7 disappearing text, element has layout so resorting to this madness!
document.getElementById('buttonMoreSections').style.backgroundColor=(document.documentElement.scrollTop % 2)? '#fff':'white';
})


total MADNESS !

Wednesday, 16 December 2009

Image Replacement, but only if images are enabled

The fact that you don't know whether the user has images enabled or not is a real problem with image replacement. You can't hide the existing text if images are disabled as the user will just end up with nothing, the standard way to handle this problem is to keep the text visible and have an image bigger than the text and place the image over the text, if the user doesn't have images they will still see the text. This is only ok if your replacing a header where the image is bigger than the text, but no good for any small images like icons where the text is always going to be bigger.

One way round this is to only add images if your sure the user has images enabled and to keep the text in the HTML flow for screen reader users.

One way of testing for images is to use JavaScript and set a class name on the HTML element if image are enabled/supported, then you can apply styles based on the existence of this class name, using this technique you'll only do image replacement if the user has JavaScript, CSS and Images enabled which is a good thing. If the user doesn't have any of these three you probably don't want to mess with the UI at all.

See the image replacement test page for some uses.


Tuesday, 10 November 2009

IE, butters text after filter is applied

IE8 logo In my little world IE8 usage over took IE7 for the first time last month, so decided to try to sort out my JavaScript fades which I disable for IE. The problem with the fades is after the fade in ends the text is blurred, fuzzy and generally butters!

TODO: add image of butters text...

I haven't looked into this in any great depth, but the problem seems to be with clearType text in IE and obj.style.filter. If you apply the Alpha(opacity) filter the text gets mashed up, so setting obj.style.filter="Alpha(opacity=100)" breaks the anti-alias on text.

One work around is to set a background colour on the element your going to apply a filter to. If you don't know which elements your going to fade in advance you can reset the filter after the fade has run, which isn't ideal but better than nothing.

I user a method similar to the bellow for setting opacity

/** @id Transition.SetOpacity */
function SetOpacity(el,opacity){
opacity = (opacity>=100)? 100 : opacity;
el.style.opacity = (opacity / 100);
el.style.MozOpacity = (opacity / 100);
el.style.KhtmlOpacity = (opacity / 100);

// private method to set opacity in IE
setIEOpacity(el,opacity)

function setIEOpacity(el,opacity){
if (!el.currentStyle.hasLayout){
// give element layout
el.style.zoom = 1;
}
if (!el.originalFilter){
// save the original filter, if one exists, so you can replace it after fade
el.originalFilter = el.currentStyle.filter || 'none';
}
// if opacity = 100 put the original filter back
el.style.filter = (opacity == 100) ?
el.originalFilter :
'progid:DXImageTransform.Microsoft.Alpha(opacity='+ opacity + ')';
}
}

NB. for a fade to work in IE at all, the element needs to have layout (obj.currentStyle.hasLayout) this is a read only attribute, to set it to true you need to give the element a style that will trigger it to set .hasLayout to true. I normally set the zoom style to 1 in an IE only stylesheet. For other ways of tringering hasLayout see haslayout.net

Wednesday, 7 October 2009

Forms, field by field error checking

Pyramidal neurons forming a network in the brainLike most developers for the past couple of years I've been implementing form checking on a field by field basis so when a user is filling in a form field the client side error checking is run. This has a couple of advantages, firstly the users gets the errors there and then, so when they get to the end of the form it should be valid and ready to submit, also it's quite fun to implement!

However, I was reading some research (which I have now temporarily misplaced!) which calls this approach into question. The basic premise was that in regard to form filling, the brain works in two different modes, the 'create' mode and the 'edit' mode.

In light of this the best way to handle forms is to allow the user to fill in the form (brain is in create mode) then validate all the elements in one go, so the user can go back over the form (brain in edit mode) to fix any errors. This was tested and found to be better than making the user switch from create to edit mode on a form field by field basis.

I'll try to find the relevant article, unfortunately my brain is in permanent 'confusion' mode so that may take some time!

Tuesday, 16 June 2009

Good bye browser matrix...

Is it time to just say "So long, and thanks for all the fish" to the browser matrix?


The browser matrix was quite handy in days of yor, when browsers worked in quite fundamentally different ways from each other (remember NS4 vis IE4?). Nice then to just pick a couple of browsers to support, you couldn't really expect your site to work on anything that you hadn't tested it on anyway.

So why don't we need it anymore?
Well, for a start if your using progressive enhancement you could expect your site will work on any user-agent that understands HTML. In which case what does "Support" actually mean? The notion that a website supports a specific browser OS combination is an old school way of looking at the web.

What's really the problem with saying that a site supports a particular browser though?
Well, let's look at what we really want from our website in the first place, we put our site up on the Internet, it's available to anyone, so we want it to work as well as possible, on as many user-agents as possible.

How should we achieve this?
The browser matrix method encourages developers to adopt a blinkered approach, we ask developers to build a site that works on browsers A, B and C. They build it using A and then debug on B and C, job done.

So where does that leave users of browser D?
Testers don't check browser D because its not on the matrix and any bugs that do get raised by users get rejected as "Unsupported user-agent" for the same reason. So, over time the browser matrix is a self for filling prophecy, the site will only work on browsers A, B and C.
There is another interesting aspect though, the notion of a browser being "Supported" changes the responsibility of the developer, if browser C has a bug in it that results in a visual formatting issue, the fact that the problem is a bug in the browser is irrelevant, because browser C is supported the developer has to accommodate the bug in the website, this means hacks and work-a-rounds in the code. The result of which means the more supported browsers in the matrix the more bloated your site gets as more and more browser bugs get accommodated for.

So what's the alternative?
The best alternative to having a browser matrix, is to NOT have a browser matrix!

Let's look at a scenario; we ask our developer to build the site to work on a text only browser, and give them a visual design or two of how the site should look on a desktop browser. The developer makes semantic, working HTML and enhances it to match the visual design, not having a matrix to work from the developer checks the enhanced look and feel on the browsers they have on their machine, likely the latest versions of the main browsers, the developer then hands the site over to testing.

The testers, without a browser matrix to follow, will start testing the site, they are likely to check the analytics of the site, or of a similar site, to see what user-agents people are using and organise their test effort accordingly, as well as that, they will test on any user-agent they have to hand, they can't help them selves!

Other people in the company will also raise bugs as the user-agent they are using is no longer "Unsupported".

Okay, so we have anarchy!
Our developer has loads of bugs assigned against the UI, how do we handle them all?

In actual fact, it's quite easy, bugs can be prioritised quite quickly by assessing a number of factors:

* What is the impact of the bug to the user? (is it a show stopper?)
* What is the frequency of the effected user-agent? (is it a high frequency browser?)
* How easy is it to fix
* What is the negative impact of fixing the bug? (e.g. do you have to introduce code forking?)

For example; bugs come in around visual layout in IE6, impact on users is fairly high, it's a high frequency browser, easy to fix and impact on other browsers is nil as we can fix issues using conditional comments for IE6 only, these can be done ASAP. So basic common sense really!

In the end the simple fact that more issues are know about will help and the site get better over time, since it won't get held back by the existence of a Browser Matrix.

Wednesday, 3 June 2009

Font size switching, yes again!

I was just having the font-size switching conversation with someone. I was banging on about our policy which is tell users how to change the font size in their browser, rather than duplicating this functionality with client side script.

All very well, but down at Shaw-Trust they (pretty much everyone) prefers the click to change method (e.g. three varying size "A"s next to each other and you click on the one you like the size of... www.webcredible.co.uk).

But, I complained, if you set the size of text you like in your browser then text in all websites sites, that aren't fixed size text, will be the right size?

Yes, but some sites don't support changing text size and break.

What do you mean? Either a site will be coded old school with fixed font size, in which case it wont change or it be relative size and will work?

No, some sites have relative font size but don't support larger sizes so if you change the font size in your browser the layout will be broken and you'll need to change it back in order to use the site.

You can't be serious, show me a site like that...

www.virginmedia.com (try IE with anything other than default font size)

oh...


IE6 Screen shot with larger and normal font size...

IE6 Screenshot with larger and normal font size

Friday, 1 May 2009

@media nasty hacks

What with IEs conditional comments its been a long time since in needed to do any CSS hacks. Being a bit out of practise it goes against the grain to add stuff that's obviously wrong just to get something to display in one browser or another. Still, what are you going to do? Telling people to change their user agent is not really a viable option. The particular problem I was trying to hack was on Opera mini on the Blackberry, so took to delivering a little CSS for that it could understand to fix the layout issue.


/* hack to fix search form in Opera mini */
@media screen and (max-device-width: 480px) {
html #search-options,
html #search-options div.searchOptions,
html #search-box,
html #search-options div.searchBoxWrapper,
html #search-options div.searchBoxWrapper div.searchBoxContainer,
html #search-options div.searchBoxWrapper div.search-btn {
height:20px;
overflow:hidden;
}
}


As you can see, fixed the heights of a load of divs which fixed the layout issue. So only problem is, this breaks they layout that worked perfectly well before in the iPhone! So I had to add yet another hack to reset it back to how it was...


/* remove for webkit */
@media screen and (-webkit-min-device-pixel-ratio:0){
html body #search-box {
height:auto;
}
}


It's like 1999 all over again! Anyway, question is, is there a better opera mini only way to write the first hack which won't get picked up by other small screen devices???

I shall investigate (at some point!)

Thursday, 26 March 2009

Mouse over menus and delay

Just been looking at menus, not for the first time nor the last. One thing i picked up on in Jakob Nielsen's Alertbox is his opinion that mouse over menus should appear after a half second delay, this is to reduce the irritating flicker you get when moving the mouse around a page. I've only seen this implemented a couple of times, can't remember where, but I totally agree with this approach.

I'm much more of a clicky menu person myself, but if you must go mouse over then, delay before showing and delay before closing too!


Related links


Saturday, 21 March 2009

JavaScript support levels

Generally, i breakdown JavaScript support into 4 levels. This makes life much easier and as i'm a lazy bas****d it suits me to keep development time down to a minimum.

I'm using something like this...


window.user = {
jsSupportLevel : 0,
init: function(){
if(!document.getElementById){return;}
var ua=navigator.userAgent.toLowerCase();
var platform=navigator.platform.toLowerCase();
var isPC = (platform.match('win32')||platform.match('win64'))?true:false;
var isDeskTop = ( ua.match('firefox') || isPC || platform.match('macintel') )?true:false;
var support = 1;
if (window.XMLHttpRequest){support = 2}
if ( support==2 && isDeskTop ){
support = 3;
}
this.jsSupportLevel = support;
}
};user.init();


This will give you a js global "user" and you use it in your js to determine whether to do some enhancements or not.
e.g.
if (user.jsSupportLevel>1){
... attach Ajax related event handlers ...
}


Example support levels

0 = user-agent doesn't understand DOM (!document.getElementById)
1 = user-agent DOM enabled
2 = user-agent has native ajax support (window.XMLHttpRequest)
3 = user-agent is probably a desktop browser

You can use the above to deliver the appropriate level of JavaScript support to users, e.g. Don't implement any JavaScript if the support level is 0, only do very basic JavaScript for level 1, do pretty much everything for level 2 and level 3 is level 2 with transitions and opacity (stuff you wouldn't want on a mobile/hand-held).

This will only work if your using progressive enhancement of course!

Thursday, 19 March 2009

Expression Web SuperPreview

Having a play with Web SuperPreview from good'ol Microsoft and I have to say i'm a little disappointed.

screen shot of Web SuperPreview

It very much aimed and a px by px comparison of two browsers, which in my experience is pretty much a waste of time. I've never had a support call were someone is upset about the look and feel being slightly different on a different browser.

Focusing on minor UI display differences results in designers insisting on using graphic buttons and DHTML scrollbars and code forking in the CSS in order to achieve pixel perfect adherence to the visual design on various target browsers. Not a road that I want to go down.

I think I'll be sticking to IETester and VMs, for now anyway.

Also, i may be missing something blatantly obvious but I couldn't workout how to trigger a control or even follow a link in the preview panel so couldn't inspect any dynamic page elements. I assume that's just me being a twat. The product would have no value at all if you can't interact with the page, but it's not immediately obvious how you do (well, not to me anyway!! : ).

Related links...


Sunday, 8 March 2009

Icons and image replacment

Generally speaking i'm not a fan of image replacement, but for inline icons it seems to work a treat. We had this issue that users with screen readers where struggling with our icons that we use liberally in our pages. For example, the "More information" icons that we use on some links to indicate that there is more detailed information available, looks something like this.

Image of an internet link with an icon

HTML view of anchor link code, containing an img tag

Although this looks quite reasonable it was causing problems for screen readers as they list out all images which means the user will get a list of images all saying "More information" (so a bit like having a list of links all saying "Read more..."). This is a problem with using images as text (an icon is really short hand text) if you look at them out of context they don't make sense.

Using image replacement as a way of hiding images from screen readers works quite well, as they are background images and not img tags. If we look at the same link using image replacement...


HTML view of anchor link code, containg image replacement code


I tend to use nested spans for image replacment, you may prefer to use a single span with a combination of overflow hidden and a text-indent value, depends what browser support your after. If you don't need backwards compatibility you could just use the CSS "content" property instead.

Tuesday, 3 March 2009

Are DOM browsers the edge case?

I think the answer might be Yes.

You want your application to work on the web so you write it to output semantic HTML, if you do this without using script (or noscript tags) your site should work on pretty much any browser that understands HTML regardless of platform, OS, browser version etc... that's quite a lot of different user-agents running on, mobiles, iTV, games consols, PCs etc... A subset of these user-agents will understand javascript to some degree, and a sub-set of these will understand DOM. If you then think of DOM browsers as a edge case (albeit an improtant one) everything falls into place.    

Friday, 27 February 2009

Is the NoScript tag the tag of the devil?

I came to the realisation today that the noscript tag is the sporn of the devil. It encourages developers to think of JavaScript in black and white, it's either on or off, the users support it or they don't. Unfortunately this simplistic approach can leave your users frustrated and thinking your site is broken. Consider this over heard conversation...


Project manager:
"Why doesn't the site work on my Blackberry?"
Developer:
"We don't support the Blackberry"
Project manager:
"but I thought you said it works without CSS and JavaScript?"
Developer:
"yes it works fine without CSS and JavaScript, we've built it in semantic HTML"
Project manager:
"so it should work on anything that understands HTML?"
Developer:
"... yes ..."
Project manager:
"so why doesn't it work on the Blackberry??"
Developer: ".... err ....


The NoScript tag has struck, pretty much all user agents support JavaScript. Much better to use progressive enhancement instead.

Consider this:


<noscript>
<p>
Important information for users without JavaScript
</p>
</noscript>
<script type="text/javascript">
if (document.getElementById){
// do funky things
...
}
</script>



We have just created a crack down the back of the sofa which has swallowed every user-agent that supports javascript but doesn't support document.getElementById or doesn't get the JavaScript in the first place. These users don't get the JavaScript enhancement and they don't get the noscript fallback : (

Tuesday, 24 February 2009

Are images enabled?

If your not sure what image replacement is, it's a technique to replace a bit of text (often an H1 level header) with an image. It's not really a very good idea, for one thing it creates a no fall back situation, e.g. you hide the text and put in a nice background image, which, if images are disabled the user can't see (oops!) and since it's a background image there is no alt text, hence no fall back, just an empty space.

So what to do? Using progressive enhancement you wouldn't want to implement something unless you know it's supported, you can use JavaScript to test if images are enabled and then add a class name to the html tag is one way to go.

Browsers seem to fall into two camps when images are turned off. Ones that don't load images at all and ones that load all img tags with an empty image. So you need to create two tests, first attach an onload function to an image you know exists, this will not fine on browsers that don't load images at all.

e.g...

var existentimg = new Image;
existentimg.onload = function(){
// if this didn't fine, images aren't enabled
}
existentimg.src = "know_to_exist.png";



As the above will fire on browsers that load images when images are disabled you need to do the same test again but this time attach the onload to an image you know does not exist.

e.g...

var nonexistentimg = new Image;
nonexistentimg.onload = function(){
// if this fires, images are disabled
}
nonexistentimg.src = "/nonexistent.gif";



I've put an example function at the bottom of this page that combines the two functions.

What to do with the info when you have it? Once you know if the user-agent has images enabled you can add a class name (e.g. "img") to the HTML tag, this will provide a way of using the information in your CSS.

e.g...

/* these styles will only be implemented if images are enabled */
html.img h1 {background-image:url(H1.png);width:XXXpx;height:XXpx;}
html.img h1 span {position:absolute;left:-9999px;}


Anonymous JavaScript function example, run this on DOM ready


(function(existingimageurl){
var existentimg = new Image;
existentimg.onload = function(){
document.getElementsByTagName('html')[0].className+= " img";
var nonexistentimg = new Image;
nonexistentimg.onload = function(){
document.getElementsByTagName('html')[0].className = document.getElementsByTagName('html')[0].className.replace(/\bimg\b/,"");
}
nonexistentimg.src="/nonexistentimg.png";
}
existentimg.src = existingimageurl || (document.images.length?document.images[0].src:'http://www.google.co.uk/images/nav_logo.png');
})();


If your using jQuery (or something similar) and you know there is at least one image on your page...


$(document).ready(function(){
(function(){
var existentimg = new Image;
existentimg.onload = function(){
$('html').addClass('img');
var nonexistentimg = new Image;
nonexistentimg.onload = function(){
$('html').removeClass('img');
}
nonexistentimg.src="/nonexistent.gif";
}
existentimg.src = document.images[0].src;
})();
})

Saturday, 21 February 2009

JavaScript support, who does, who doesn't?

JavaScript or lack of is a thorny issue, but if we take a quick look at some of the user groups that this effects:

1: User agents that don't have JavaScript enabled. As a lot of user tracking software relies on JavaScript to work so it's hard to tell how many users actually don't have JS, but in the beginning of 2009 it could be about 6%. Made up of user-agents that don't support JS, user-agents that have it turned off as part of company policy and user-agents routed though a proxy (e.g. public proxies normally don't support JavaScript)

2: User-agent that you haven't tested your JavaScript on. Just because a user-agent supports JavaScript doesn't mean your script will work (ever done this: if (!document.getElementById){return} ? ), even the four (webkit/moz/opera/ie) main desktop browsers diverge radically in some areas, meaning that there is a good chance that you scripts may not work on anything you haven't tested them on.

3: User-agents where your script is meaningless. e.g. if the user is using a small screen (or zoom software) and they click on a DHTML link that moves some page content from out of view left (eg. margin-left:-9999px) to out of view right (e.g. margin-left:800px;). In these cases JavaScript is no help to the user.

4: Assistive technologies: Most assistive technologies like screen-readers run on the top of your normal browser so although the browser understands JavaScript the assistive technology may not. They may not see things you have written in with JavaScript rendering your JavaScript content inaccessible.

There are probably other user groups effected, will add when they come to mind...

So our user groups fall in to a number of categories...

1. users with browsers that don't support JavaScript at all, or have JavaScript turned off
2. users with browsers can't run your scripts (they error or don't support DOM)
3. users accessing your site though anal proxy, firewall etc...
4. users for whom your JavaScript is redundant (not really any help)
5. users for whom your JavaScript is actively blocking access to content
 

Note: the opinions on this page don't represent that of my employer in anyway. And, actually only vaguely represent my opinions on the date of publication. (Unlike Ms T, I am very much for turning)