Pixelastic

You can clip our wings but we will always remember what it was like to fly.

Posts from August 2010

CSS for Javascript enabled browsers

In a previous post I was wondering if my way of loading js-specific CSS rules was right. I was loading a specific css file for js overrides.

This allow for lighter stylesheets for users without Javascript (they won't load useless rules), but also resulted in slower rendering for other users because they needed to load two stylesheets instead of one.

After much pondering, I now think that loading all the rules in one stylesheet is the way to go.

In addition with the number of request mentionned, keeping all your rules in the same file is much more easier to maintain. Js-specific rules aren't that big either so the small additional bytes are usually better than a full http request.

I now prefix all my Javascript rules by .js. I add a js class to the html element directly from the head, this helps in preventing any FOUC that may occur because we add the class before the body rendering.

<head>
    <script>document.documentElement.className+=' js';</script>
</head>

 

How to prevent a FOUC but still gracefully degrade the jQuery UI tabs

I finally managed to fix something that was bugging me for a very long time. I'm talking about the jQuery UI tabs.

They are pretty useful actually, but I always hated that FOUC they produce. For one split second, you'll see all your tabs content, then they'll be neatly re-arranged in tabs.

What I want are my tabs displayed correctly right away, on first load.

First, the markup

Here's a typical tabs markup :

<div class="tabs">
    <ul>
        <li><a href="#firstTab">First tab</a></li>
        <li><a href="#secondTab">Second tab</a></li>
    </ul>

    <div class="tabPanel" id="firstTab">
        First tab content
    </div>
   
    <div class="tabPanel" id="secondTab">
        Second tab content
    </div>
</div>

CSS to hide all tabs if Javascript is disabled

If your Javascript is disabled, so will jQuery UI. We will then hide the <ul> because it serves no purpose here. We will only show it if js is enabled

.tabs ul { display:none; }
.js .tabs ul { display:block; }

Applying jQuery UI tabs

By doing a $('.tabs').tabs(); jQuery UI will treat your <ul> as your tab menu and all your .tabPanel as their corresponding contents. It will hide all your panels, except for the first one. It does so by adding a .ui-tabs-panel class to every .tabPanel as well as a .ui-tabs-hide to every panel it hides.

Right now, you should add another CSS rule to hide the unused panels :

.ui-tabs-hide { display: none; }

But if you look at your page now, you'll see all your tabs content before they get hidden. That is the FOUC I was talking about. The jQuery UI documentation indicate that to remove it, you should directly add the .ui-tabs-hide class to panels you'll want to hide.

As also pointed in the doc, it will not gracefully degrade because users without Javascript won't even be able to see your other tabs. Also, it asks you to add server-side logic (HTML markup with jQuery specific classes) for something that should be handled entirely client-side.

Removing the FOUC while gracefully degrade

Ok, so what I did was writing two simple rules that will directly hide all unused panels while still displaying the active one, even before jQuery UI takes action.

.js .tabPanel + .tabPanel { display:none;}

That way, no FOUC, and users without Javascript still see all the content. Unfortunatly, if you now try clicking on your tabs, you'll see that nothing happens and you get stuck with your default panel always visible.

Fixing the jQuery UI tabs

As jQuery will add new classes to my elements, I'll just have to write more specific rules that use those classes. Here's the little piece of logic I came up with :

.js .tabPanel.ui-tabs-panel { display:block;}
.js .tabPanel.ui-tabs-panel.ui-tabs-hide { display:none;}

All the jQuery panels are shown, except for the one hidden by jQuery. All those rules being more and more specific, they will get applied once the tabs are activated but still override the previous display: declarations.

Conclusion

This is how I fixed an issue that was bugging me for years. Unfortunatly the CSS rules being dependent on the markup used, I haven't yet been able to write them in a global form that could be added to the main jQuery UI css files.

Text emboss in CSS

I know only two unperfect ways of displaying an emboss text in CSS. As we can't use inset shadows with text-shadow, unlike box-shadow, we have to resort to other hacky ways.

Let me show you what can be done.

Simulate it with an optical illusion

Instead of creating a real inner shadow, you can just create an optical illusion by adding the correct shadow to make the viewer believe your text is embossed.

If you have dark text, just add a subtle white shadow at the bottom, while if you have light text, just add a subtle black one at the top.

<a class="emboss light">Lorem ipsum</a>
<a class="emboss dark">Lorem ipsum</a>
.emboss { font-size:1.2em; padding:50px; font-weight:bold; background:#0A539C; display:block; }
.light { color:#FFFFFF; text-shadow:0px -1px 0px rgba(0,0,0,0.9); }
.dark { color:#000000; text-shadow:0px 1px 0px rgba(255,255,255,0.1); }

I added an emboss class to more easily spot the effect, but the important code is in the light and dark rules.

Add an almost transparent layer

For this second solution we will simply add a second layer with the exact same text above the first text. Just slightly moving it and making it partly transparent.

You could add twice your element in your markup, but that would be bad for accesibility as well as SEO, so we'll instead use the :before pseudo-class.

We can set the :before content using the content: property, and we can also use the attr() method to get the content of one of our element's attributes. We just need to put the exact same text in one of our attributes and we're good to go.

This can be easily done with a link and its title attribute.

<a class="emboss lightAgain" title="Lorem ipsum">Lorem ipsum</a>
<a class="emboss darkAgain" title="Lorem ipsum">Lorem ipsum</a>
.lightAgain { color:#FFFFFF; position:relative; }
.lightAgain:before {
    content: attr(title);
    position:absolute;
    color:rgba(0,0,0,0.1);
    top:-1px;
    left:-1px;
    padding:50px;
}
.darkAgain { color:#000000; position:relative; }
.darkAgain:before {
    content: attr(title);
    position:absolute;
    color:rgba(255,255,255,0.1);
    top:1px;
    left:1px;
    padding:50px;
}

The effect is much better with this technique but it also has it share of drawback :

  • You have to write your content twice, once in the element and once in an attribute
  • You have to copy all your padding rules in the :before element or it won't get correctly positionned
  • You can't select the text with this effect

Hope those two little techniques helped someone, anyway.

How to hide/show elements based on Javascript

Sometimes you have a really great design with some really fancy Javascript goodness, like drag'n'drop and other shiny Ajaxy stuff.

But when you browse the website with Javascript disabled, none of that works and you end up with interface elements that should'nt be here because they do not work.

In those case, you'd rather display a nice message to your user, telling him that he can't use the feature withou Javascript enabled.

But how do you do that ?

Well, I myself load two different stylesheets. Remember that your website should be working without Javascript, this is just the last layer you add.

My default stylesheet will load all the rules for when Javascript is not enabled. No fancy cursor:move here.

Then I load a second stylesheet using Javascript, using document.write() in the <head>. And that's in this one that I write rules that overload the previous one. I add every styling that deals with Javascript-enabled features here.

Limitations

I may be changing the way I load the JS stylesheet in the future. I don't really like relying on document.write because it is evil. I also don't like the idea of getting one extra request.

I could add a js class to my body element (like modernizr does with all its CSS3 properties) and then target elements by prepending .js to the rule.

But it means adding rules in my main CSS file for users without JS that will still be downloading those extra useless bytes.

I haven't yet figured which way was the best (or should I say, the worst)

Convenient methodes

Whatever way you choose, one thing that really helped me was two real simple classes : jsOn and jsOff that I add to elements.

Elements with jsOn will only be visible if Javascript is enabled and hidden otherwise, while element with jsOff will do the opposite.

Assuming you mark your body element with a js class if Javascript is enabled, here's how to do it :

.jsOn { display:none; }
.js .jsOn { display:block; }
.jsOff { display:block;}
.js .jsOff { display:none; }

Hope all that helps at least someone.

Corrupted strings when using strtolower()

Little blog post at 5am, I'll try to make it very short, I need some sleep.

To avoid getting corrupted string results when calling strtolower on UTF8 strings, you can call mb_strtolower instead. The mb_* functions are aware of the utf8 encoding.

Unfortunatly, sometime you just can't, because the call is made in the cakePHP core (Inflector). Defining a CTYPE locale for your whole app may be a better solution.

Just add a setlocale(LC_CTYPE, 'C'); in your app and all your utf8 strings will correctly work with string functions.

Just note that on Windows, calling setlocale will change the locale for all threads, not just the one where PHP is running. This may cause unexpected results.

How to fix the Firefox autocomplete 'feature'

Firefox can be quite a pain sometimes. I usually praise this browser features but today I had to deal with one tricky 'intelligent' feature.

You know that autocomplete/autofill feature, so that when you enter your user name, Firefox automatically fill the corresponding password field so you won't have to do it ?

Well, it is bad, and here's why.

How does it work ?

I haven't read the source code, but from what I've experienced it seems to work like that :

For each password field, FF will find the related username text field. This is just a very basic search, it assume that the closest previous text field is the related one. Now, everytime you enter a value in the text field that match one of the username saved, it will autofill the password field.

Note that if you have more than 5 password fields on a page, the feature will be disabled.

What is terribly wrong with this approach

Let's imagine you are a registered user of a website. You want to update your preferences. You have a nice page with lots of input fields where you can change your email, password and date of birth.

For security reasons, your password is not displayed. For convenient reasons, the website also don't ask you to type your password everytime your want to update your settings. It assumes that if you let the password field blank, it means that you don't want to change it. And if you really want to change it, then it asks for a confirmation, to make sure your correctly typed it.

So, with this autocomplete feature, if your email is also your login for the website (as it is often the case), Firefox will autofill the password field with your password. Meaning that if you submit your form, you'll be met with an error because your password and it's confirmation does not match.

You'll then have to manually clear the content of the password field everytime your want to edit your profile. Which is counter-intuitive. The whole convenient method of not changing the password if left empty is rendered useless.

The worst part is that most users won't understand when the error comes and will blame the webdeveloper.

Solutions that does not work

First, Firefox is just doing a very wild guess on that fields. And, there is no way to disable this feature from the page markup. Sure, as a user you can disable it in your browser setting, but as a web developper, you can't disable it.

There is a non-standard autocomplete="off" argument, but it does not work in our case. It is supposed to prevent the browser to store previously entered values on sensible fields (like credit card numbers).

I've tried putting this autocomplete attribute on every input field, even on the form itself. Setting the value attribute does not work either. Even updating the value through Javascript would not work because the password field will get re-populated on every blur event on the text field.

A solution that does work

The final solution I've found is to add a dummy password field between your real mail and password fields. That way, Firefox will autocomplete this dummy password instead of the real one.

Unfortunatly this adds clutter to your markup, but with a little display:none in CSS it won't ever be seen by your users.

7 advices to start using OOCSS as a coding practice

Not long ago, I stumbled across this video about massive and unmaintainable CSS files from Nicole Sullivan. It made me want to try OOCSS.

OOCSS is a way to code css in a way that replicates the goodies general OOP told us. Classic CSS use the cascade in selectors to emulate a concept of inheritence.

OOCSS puts the concept of inheritence a little step further by telling us to create "modules" in our CSS files. Each module represent a "widget" of our page, or should I say of our whole site.

It comes with a simple list of principles that could allow one to write much more flexible and maintainable code. I rewrote the whole CSS for this website (as well as the admin interface I'm using behind the doors) following those principles, and I thought I'll share it with you.

Drop the #id selector

We won't be using the #id selector anymore (except for some edge cases). Instead of #commentList {} we'll use .commentList {}

This change alone lets us reuse the same element multiple times on the same page.

Note that we will still add ids in our HTML markup for form inputs or for Javascript purpose (document.getElementById is still the faster selection method).

Don't make your selector dependent on the page structure

We won't style our elements based on where they will be displayed in the page.

We don't care if our module will be displayed in a footer, sidebar, specific page or whatever. We just style it all the same.

Instead of writing .sidebar .lastComments {}, we will go for the shorter .lastComments {}.

This will allow us to easily display the last comments on any page, anywhere. You should carefully name your modules to something that clearly evoke what the module is supposed to display, without ambiguity.

If you need to tweak the display of an element in a certain part of the page, you could always write a specific selector for this specific need, that will overwrite the default rules. But this must not be your default selector.

Do not use both tag and class selector on the same element

Avoid the div.errorMessage, ul.userList selectors. The first one is just too specific : what happen if you ever want to style a <p> instead of a <div> ? The second one is useless, <ul> is a list by definition.

There is only one moment when being that specific can be useful, it is when you need to overwrite, for a specific element, rules defined for the class. Fortunatly, thanks to the OOCSS coding style, this won't happen much.

Don't make your selectors over-specific

This is a followup of the previous rule, but don't write overly specific rules, like .userList table thead tr th a. A simple .userList thead a is enough.

First, you are overly detailing each level of the markup while most of the time the uppermost and lowermost parts are enough. But you also define useless selectors like tr th or ul li, where th or li are enough (those elements can not be placed in any other parent element).

Create your own reset stylesheet

To avoid repeating margin:0; padding:0 over and over in your stylesheets, you should spend some time finding a reset stylesheet that you like and then tweaking it to fit your coding practice.

I used to include the Tripoli framework in my previous projects but I found that I had to reset styles it was settings far too often.

All reset stylesheets are not equals, some will just remove all styling from elements, letting you define them as you want. Others will also assign default rendering styles to make something both visually beautiful and cross-browser.

But the best reset stylesheet you'll get is the one you'll create (or, as it is quite a bit of work, tweak) yourself.

Use a grid system to place elements

Some years ago, I found CSS Framework like Blueprint to be a waste of time. I didn't want to clutter mu HTML markup with non-semantic classes for handling the styling.

I also found that the psd files I was given to integrate couldn't fit in grids because the sidebar was 173px wide for example.

Now I still don't think cluttering the HTML with span-6 pull-2 is the best thing that happened to CSS, but I found it much better than cluttering my CSS with endless overflow:hidden and float:left; margin-right:10px declarations.

And I still got design from not-so-professional designers to integrate that do not seems to have any logical proportion, and can't fit them in grids. But I also work with more talented people that deliver beautiful design and they tend to be the one that fits easily into grids.

So it may not always be possible to use a grid system, but more often than not, it is and even if that means tweaking the original (badly designed) design a dozen pixels off, it will greatly help the CSS process.

Create a global stylesheet for your main classes

I also create a main stylesheet for all the classes I know I'll be reusing all accross this project (and others). I include it at the top, right after the reset one.

In it I'll defined general classes that could be extended by all the other inner classes later. Like a .message class, that I may be extending later using .message.error or .message.success

One final word

Ok, this is it. I'm fairly new to OOCSS too, so I'm still discovering it too. So far I found that I have greatly reduce the size of my files but most importantly, my styles are way more easy to tweak for special needs than before.

It also helped me separate the various elements of the website, and it is much more easier to find the piece I need. Fewer classes to depends on means I know where the rules are coming from and I can write more specific selectors if need me more easily.

Testing a custom ErrorHandler in cakePHP

I just finished writing the test case for a custom AppError class. Writing the tests case was indeed much more difficult than writing the class itself.

Here are some things I've found that may interest anyone wanting to test their own ErrorHandlers. Most of the ideas are taken from the core ErrorHandlerTest.

Anatomy of an error

Every class in cakePHP extends Object. And in Object is defined a method named cakeError. It means that anywhere in your app you can call $this->cakeError() and the whole app will stop and display the specified error.

What cakeError does is instantly create an instance of either Controller or CakeErrorController, find the correct view, render it, and stop.

Because of the use of static variables, exit calls and other happyness, testing several errors in a test case needs us to define some new classes to shortcircuit most of the logic.

Preparing the test

This step is actually pretty short. Just load the default ErrorHandler by calling App::import('Core','Error');

Creating a dummy AppController

Then, you'll have to create a new AppController class that extends Controller.

We will override the header method to prevent 'Headers already set' error when calling several errors in a row.

We will also override the render method to store in an inner property the name of the rendered action. It will help test that the correct error is rendered.

You may also need to define a custom list of helpers if your custom views are using any custom helpers.

Here is what it looks like on my side :

class AppController extends Controller {
// Helpers used in the view. If not set, will generate a fatal error
var $helpers = array('Caracole.Fastcode', 'CaracolePacker.Packer');
// Overriding the header method. If not set, will generate 'Headers already set' errors;
function header($header) {
$this->header = $header;
}
// Overriding render method to keep track of the rendered error
function render($action) {
        $this->renderedAction = $action;
        return parent::render($action);
}
}

Creating a TestErrorHandler

This class will extends your AppError. It will just overwrite two methods to make it correctly work in a test case.

First, we'll need to overwrite the __construct. The default construct will set the inner $this->controller property to either a Controller or a CakeErrorController instance depending if its the first error fired or not. I must admit that I haven't really understand the difference between the two. But I know that CakeErrorController extends AppController while Controller extends Object.

And as we need to overwrite methods of this property, it being a CakeErrorController is great, while it being a Controller is bad. Anyway, what I did was copy/paste the parent __construct into TestErrorHandler and just force it to always create a CakeErrorController instance.

The other method we need to overwrite is the _stop. If we don't, the whole script will halt after the first error you'll test.

So, enough talk, here's the code :

class TestErrorHandler extends AppError {
// Copy/paste of ErrorHandler construct method, but force a new instance of CakeErrorController as $this->controller each time
// CakeErrorController extends AppController, so we can overwrite its methods
function __construct($method, $messages) {
App::import('Core', 'Sanitize');

// Forcing CakeErrorController
$this->controller =& new CakeErrorController();
$options = array('escape' => false);
$messages = Sanitize::clean($messages, $options);

if (!isset($messages[0])) {
$messages = array($messages);
}

if (method_exists($this->controller, 'apperror')) {
return $this->controller->appError($method, $messages);
}

if (!in_array(strtolower($method), array_map('strtolower', get_class_methods($this)))) {
$method = 'error';
}
if ($method !== 'error') {
if (Configure::read('debug') == 0) {
$parentClass = get_parent_class($this);
if (strtolower($parentClass) != 'errorhandler') {
$method = 'error404';
}
$parentMethods = array_map('strtolower', get_class_methods($parentClass));
if (in_array(strtolower($method), $parentMethods)) {
$method = 'error404';
}
if (isset($code) && $code == 500) {
$method = 'error500';
}
}
}
$this->dispatchMethod($method, $messages);
$this->_stop();
}

// Preventing the error from stopping all the request
function _stop() {
return;
}
}

Writing your tests

Ok, your almost done. You just have now to write your tests. They have to follow a special syntax to correctly work.

First, you'll have to wrap your ErrorHandler creation between ob_start() and $output= ob_get_content(), otherwise you'll end up with error popping right into your test case because the ErrorHandler force the controller to render the view.

You'll be able to access interesting properties through $errorHandler->controller->renderedAction and $errorHandler->controller->header. You can also directly test the view output through $output.

Ok, so here's one of my tests :

// DEV : Error will use the error layout
function testCallingErrorInDevWillUseErrorLayout() {
ob_start();
$errorHandler = new TestErrorHandler('missingController', $this->errorParams);
$result = ob_get_clean();
$this->assertEqual($errorHandler->controller->layout, 'error');
}

Conclusion

It took me some hours to glue all this pieces together, I hope it may be useful to others, too. Writing the AppError itself was way easier, but as I'm now test infected I don't imagine writing code without the corresponding tests.

Correctly instanciating models in components

I sometime need access to a specific model in a component. Say a User model for checking user rights in an Auth component. In that cases, I just create an instance of the model by calling a $myModel = &ClassRegistry::init($myModelName). It works perfectly.

Today I found that it wasn't working exactly as I wanted. In fact, behaviors used by models loaded that way are not completely set up. All their callback methods (beforeFind, afterFind, beforeSave, afterSave) works as expected, but the main setup method is never called.

This caused some havoc in my app because some reaaaaally important stuff was defined in the setup method

There I fixed it

I just manually fired all the setup methods by calling a simple :

foreach($myModel->Behaviors->_attached as $behaviorName) {
$myModel->Behaviors->{$behaviorName}->setup($myModel);
}

Once again, small fix, but does the trick. I did not file a bug report because I'm not really sure this is bug or if that is so by design.

Disabling a Behavior from inside its setup() method

Sometime you write a nice Behavior, that will automate a lot of stuff that almost all your models will need. But, unfortunatly, a couple of models won't need it. Actually, attaching it to them will even break your code.

So what do you do ? Do you resort to manually fill the $actsAs variable of each model, except for the two lonely loosers, or defining the $actsAs of your main AppModel really seems more appaling ?

Lazyness to the rescue

If, like me, you prefer writing less code, you'd probably go with the AppModel approach. All you have to do is define a setup() method in your Behavior and check if it is applied to the right or wrong type of model. Fortunatly for us, the $model is passed as first argument.

Once you've identified the faulty models, you just have to disable the behavior for them. The BehaviorCollection that comes bundle into $model->Behaviors has a nice couple of enable/disable methods. Unfortunatly, they won't work from inside the setup() method because the Behavior is not yet correctly instanciated.

What you can do, however, is to hack inside the BehaviorCollection to update the inner _disabled key to add your own Behavior to the list.

function setup(&$model, $config = array()) {
        [...]
if ($faultyModel) {
        $model->Behaviors->_disabled[] = 'MyBehavior';
}
}

This is more than a bit hacky, I have to admit that. But it does the trick. Enjoy.

Previous12