Does CSS load resources even if they aren’t used?

Let’s say we have a huge stylesheet that hasn’t been cleaned up in a while. A feature is removed from the site by the CSS is left in. This CSS contains background-image references. Are these images loaded by the browser even though no element is using them?

Short answer: NO

But what about elements that are not on the page on page load, but are post-loaded in Javascript?

I set up a quick experiment to test this:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8"/>
    <title>CSS Resources Test</title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
</head>
<body>
    <h1>CSS Resources Test</h1>

    <h2>Javascript post-loaded image</h2>
    <p>Clicking the button below will append a new image to the DOM.</p>
    <div id="normally-loaded-image"></div>

    <div id="button">LOAD NEW IMAGE</div>

    <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js'></script>
    <script>
        $(document).ready(function() {
            $('#button').click(function() {
                $('<div id="post-loaded-image"></div>').insertAfter('#normally-loaded-image');
            });
        });
    </script>
</body>
</html>

 

#normally-loaded-image {
    width: 300px;
    height: 300px;
    background: #0C0;
    background-image: url(http://wpmu.org/wp-content/uploads/2012/07/Responsive-WordPress-theme.png);
}
#post-loaded-image {
    width: 300px;
    height: 300px;
    background: #0F0;
    background-image: url(http://bloomwebdesign.net/myblog/files/2013/03/responsive-website-design-tips.jpg);
    background-size: cover;
}

When I click the button, JavaScript will inject an element with styles referencing a new image.

If I look at Network inspector in Chrome, I can see that the post-loaded image is only requested onClick, even though it is specified in the CSS.

post-loaded-image-network-inspector

I can also confirm that the experience is the same for styles which are inline and in the head.

What about media-queries that change the background-image of the same element?

So I added a new element and styles to my test:

    <h2>Responsive Image</h2>
    <p>This element's background-image will change at widths higher than 1000px.</p>
    <div id="respond"></div>
#respond {
    width: 300px;
    height: 300px;
    background: #0C0;
    background-image: url(http://wpmu.org/wp-content/uploads/2012/07/Responsive-WordPress-theme.png);
    background-size: cover;
}
@media (min-width: 1000px) {
    #respond {
        background-image: url(http://bloomwebdesign.net/myblog/files/2013/03/responsive-website-design-tips.jpg);
    }
}

It turns out media-queries also behave the same way! Only when I drag my window larger than 1000px does the new image get requested.

Demo: http://jalproductions.co.uk/includes/scrapbook/css-resources-test/

One comment so far, add another

RTL Solution

Sets up flip function and variables for RTL layouts.

See (https://github.com/jamesl1001/RTL-Sass#readme) for a list of properties that need to be flipped.

Interpolation should be used for any property which has a direction (e.g. padding-left, margin-right, border-right, left, etc.)
flip() should be used for all other properties

@function flip($value_ltr, $value_rtl) {
  @if $rtl { @return $value_rtl; }
  @else { @return $value_ltr; }
}

$padding-left:    padding-left;
$padding-right:   padding-right;
$margin-left:     margin-left;
$margin-right:    margin-right;
$border-right:    border-right;
$left:            left;
$right:           right;

@if $rtl {
  $padding-left:  padding-right;
  $padding-right: padding-left;
  $margin-left:   margin-right;
  $margin-right:  margin-left;
  $border-right:  border-left;
  $left:          right;
  $right:         left;
}

// Usage examples:
// .class {
//   #{$padding-left}: 8px;
//   #{$padding-right}: 8px;
//   #{$margin-left}: 8px;
//   #{$margin-right}: 8px;
//   #{$left}: 8px;
//   #{$right}: 8px;
//   margin: flip(1px 2px 3px 4px, 1px 4px 3px 2px);
//   float: flip(left, right);
// }

// LTR output:
// .class {
//   padding-left: 8px;
//   padding-right: 8px;
//   margin-left: 8px;
//   margin-right: 8px;
//   left: 8px;
//   right: 8px;
//   margin: 1px 2px 3px 4px;
//   float: left;
// }

// RTL output:
// .class {
//   padding-right: 8px;
//   padding-left: 8px;
//   margin-right: 8px;
//   margin-left: 8px;
//   right: 8px;
//   left: 8px;
//   margin: 1px 4px 3px 2px;
//   float: right;
// }
One comment so far, add another

Something I didn’t know about element selectors

I didn’t realise that element functions can only be called on elements that have been selected using the same library.

I used to think that, since jQuery is essentially Javascript, that I would be able to use native Javascript functions on jQuery objects, but obviously not the other way round. But this is not the case.

For example, I cannot use the native Javascript className function on a jQuery element $('div'). I would have to select this element using native selectors like querySelector or getElementById.

One comment so far, add another

Event delegation and black listing

Whilst finally getting round to cleaning up the Rolling News JavaScript, I realised something about the way we are handling event delegation.

Event delegation is where an event, such as a click, is handled on a higher up, container element. Events can then be triggered on any of the child elements.

However, as with Rolling News, I’ve found that I am having to black list and white list specific elements in order to exclude or include them in the event respectively.

It seems illogical to both black list and white list elements. James is looking into simplifying the event handling as a whole

I am looking into cleaning up the transitions by using max-height instead of height and transitionEnd to allow the content to reflow properly after transitioning.

Leave the first comment

March update

March mainly consisted of finishing off Afrique and Hindi, and then moving on to Mundo.

I ended up throwing myself into picking up some of Bogdan’s PHP work to do with supporting audio.

Bulletins

There was also a lengthy discussion about bulletins for Hindi. It turned out that creating a static MAP page proves rather difficult, so we had various options, none of which were solid nor doable within the time scale.

Then we ran into further problems when we realised that Afrique also had bulletins, which we were not aware of, making things more complicated.

In the end, it was decided that we would reskin the old stack pages as an interim solution while the Programmes pages are build.

Mundo

As with Afrique, I did the majority of the setup for Mundo. This was all relatively simple.

There were a couple of issues:

  • We still have them problem of not being able to fully test social shares that crawl for information until the redirects have gone in.
  • Rolling News was hardcoded in several places for Russian. Spent some time with Stefano sorting those out.

Setup steps wiki

Jordi mentioned during the Mundo development that the Jira tickets are quite disorganised. I took it upon myself to write up a Wiki page for him detailing exactly which tickets are needed, in what order, containing what, and adding some that were missing.

Code reviews

I’ve also been doing quite a lot of code reviews in between pieces of work this month; mainly CSS code, as that is what I have enough knowledge in to be able to review.

What have I learnt? Code reviews aren’t just about picking out little things like a missing semi-colon or a place where they could have used CSS shorthand for example. It’s also about looking at the code as a whole and seeing if that is the best way to do it.

Good code reviews, I find, also provide suggestions and examples of how to do something differently, not just saying, “I don’t like this, do it differently”.

Shared codebase inheritance

Whilst having my code review hat on, I was taking a look at our codebase as a whole. When we are building our new components, we are very concious about ensuring that do not load for News. This is good and follows mobile first methodology.

However what we haven’t yet taken into account are all the styles and scripts which we have inherited from News that we aren’t using.

Now, I’m going to make a sweeping, but probably quite accurate) statement and say that on average, World Service end users are more likely to have less advanced phones than UK users. This means that it should be more of a priority for us to ensure that our users are only getting the bare minimum of what they need to load our pages, not all this other gumf that isn’t being used.

I began working on a solution to this for the CSS but never finished because Mundo took priority. Hopefully I’ll get some time to look at this soon!

Leave the first comment

Problems with RTL flipping CSS using overrides

Some CSS properties are easier to override for RTL flipping than others.

Easy

Float and clear are very easy. Text-align and text-indent are fine. Any property that is defined with one value is easy to flip.

For example, float can be either left or right.

float: left;

@if $rtl {
  float: right;
  // This will completely override the original rule
}

Shorthand styles are also easy to flip, since all the values will be overridden. Given padding is used:

padding: 1px 2px 3px 4px;

@if $rtl {
  padding: 1px 4px 3px 2px;
  // We can simply swap the left and right values to achieve a clean override
}

Medium

The more tricky ones are left and right. These don’t override cleanly because both can exist simultaneously. Take the following example:

left: 0;

@if $rtl {
  right: 0;
}

The compiled CSS for the LTR stylesheet would look like this:

left: 0;

Fine. However the RTL stylesheet will look like this:

left: 0;
right: 0;

See the problem?

To resolve this, you must replace the LTR value with auto. Auto is the default value for left and right. However this does mean an extra style whenever left or right occurs.

left: 0;

@if $rtl {
  left: auto;
  right: 0;
}

Hard

Any longhand styles such as margin-left, margin-right, border-left, border-right, and so on behave in the same way as left and right. They are slightly harder however, because we cannot simply reset the original value to 0. Take the following example:

margin-left: 10px;

@if $rtl {
  margin-left: 0;
  margin-right: 10px;
}

So here we have swapped the left and right margin and reset the left to 0. However, what if the margin-right was being set elsewhere. We have just reset it to 0 without taking into consideration other styles which may be contributing to the same element. This is something to be careful with.

Impossible

background-position is impossible to flip if a numeric value has been used for the horizontal positioning.

It is very common to use background-position to nudge a background-image across if it is misaligned, however this causes issues if we need a RTL stylesheet.

This is because the functionality to align a background image on the right with an offset is missing from the CSS specification. We cannot say I want to align my background image 5px from the right.

The solution is to always set the horizontal value as left or right, and use padding on a parent container to align the background image.

Lessons

  • Use shorthand where possible
  • Don’t use numeric values for laying out a background-image horizontally
Leave the first comment

Problems with RTL Sass

I’ve realised a major flaw in the use of my RTL-Sass repo for our project.

The code itself works and would be fine for a small-scale project, however in such a large-scale project like this one, it might not be practical.

Here is a snippet of the code to show how it works:

// ===========================
// = Left/right positioning =
// ===========================
@mixin position-h($value, $origin: left) {
  @if $origin == left {
    @if $rtl { right: $value; }
    @else    { left:  $value; }
  } @else {
    @if $rtl { left:  $value; }
    @else    { right: $value; }
  }
}
// Shorthands
@mixin left($value) {
  @include position-h($value, left);
}
@mixin right($value) {
  @include position-h($value, right);
}

This snippet is used to flip the properties left with right and right with left.

It can be used like so:

$rtl = true;

#foo {
  @include left(8px);
}

Which would compile to:

#foo {
  right: 8px;
}

Relatively simple!

However, in order to make this work, you would have to replace all flippable properties to use these mixins. This in itself is fine, it would take a bit of time, but it’s doable.

The problem is that we are working on a project with 4 different teams. It would mean that all of those teams would have to use the mixins in all the new CSS they write, instead of writing normal CSS. Therefore I’m not sure this solution is practical.

How do the desktop sites do it?

The current desktop sites use CSS overrides to accomplish RTL layouts.

A ‘rtl’ class is attached to the <body> for all RTL services. The CSS hooks then onto this class to apply the overrides.

This breaks quite a few good practices: performance, maintainability, scalability and probably a few more!

However, this option seems more practical than the above solution.

I plan to see how many overrides actually need to be used to achieve RTL; if there aren’t too many, the bad practises won’t be too bad.

I’m also going to ask around for some other solutions to see if I’m looking at this issue from the wrong point of view.

2 comments so far, add yours

Hindi and Afrique

Hindi and Afrique are the next two services we are releasing after Russian.

There are no new features for these services so they were super quick to finish.

I did the majority of the work for Afrique and it took me around 3 days.

Other services will most likely take longer because we will either have to develop new features or take time to retrofit already developed features.

Leave the first comment

Javascript Advanced Training

Regular Expressions

Device/Feature Detection

Constrained Devices

Third Party Frameworks & Libraries

Common Design Patterns

SE Best Practices

IDEs

New technologies

Leave the first comment

HTML5 Training

New, modified and deprecated elements and methods

  • classList – contains(), add(), remove(), toggle()
  • getElementsByClassName

 

Forms

Validation - required, autofocus, placeholder, novalidate

Web storage

  • Session storage – per session
  • Local storage – stored FOREVER

Legal cookie laws still apply

Graphics

  • Canvas
  • WebGL
  • SVG
  • Web fonts
  • CSS3

 

More

  • Offline apps
    • Server must be able to serve MIME type of cache/manifest
  • Geolocation
  • <device>
  • File reader
  • History object
  • Eventsource
  • Web sockets
  • Microdata
  • Web workers
Leave the first comment