Archive for the ‘technology’ Category

Three-way CSS-only selector

Thursday, February 11th, 2016

I was at the Slovenian CSS Meetup yesterday where I did a short talk on Quantity Queries. That’s a name given to a technique where you use pseudo class selectors to discern how many elements are inside a certain element – for example :first-child:nth-last-child(4) {} will only select the first child if there are exactly 4 elements. If you group that with the general sibling selector ~, you can also get all the other elements.

The idea of quantity queries has been around for a year or so and even though at first glance you might say “We’ve got flexbox for that now”, you’d only be right in certain cases. The thing that quantity queries bring to the table is the idea of being able to change the styling depending on the number of elements, which I guess people currently solve either on the backend or with javascript.

But that’s not the main reason I’m writing this – it’s the last talk of the night where a three-way selector solution was presented by Gorazd. He created a CSS version but had problems with the smoothness of the animation as he didn’t know where the selector was before the selection to move it to the selected position after a user interaction. He resorted to using javascript that basically only did some class switching. This immediately gave me an idea that a sibling selector could be used for that if the indicator had the same parent as the inputs and was positioned after them in the code. And today I made a proof-of-concept solution I’m calling “the three-way CSS-only selector”.

It uses three radio buttons, so the form is perfectly submittable, the labels also select properly, it animates properly and does not use javascript. It’s only been tested on the browsers I have on my Mac, so I can easily see it breaking in IE or mobile browsers – if anyone wants to fix that please go ahead and ping me to add your solution to this post. You can also check the solution on JSBin.

Preloading resources

Monday, October 3rd, 2011

You might have read that when optimizing your site for speed HTTP requests are something to look at – less requests mean less waiting and usually a faster site. When doing that for CSS you’re faced with a dilemma:

  1. Put all of CSS in one file, which means less requests, but also means that the first time people land on your site, they will wait longer than needed to see the content which could mean a ruined first impression.
  2. Put CSS in per-section files, which means less loading up front but more request as people drill down into different sections of the page.

When using the first option your only focus can be on how to optimize the CSS so that it loads as fast as possible and is as small as possible. As you have less files you can easily have that file minified and gzipped even if you’re not using a deployment solution that will do that for you.

Using the second variant gives you more options – you should still optimize the files, but now you have the option of deciding when to load them to make sure that the landing page does not get hit by the added request.

Even though I started with CSS preloading can be done for any resources needed on the page – fonts, scripts, images.

When to preload?

An very obvious case is a search results page. It usually has a very distinct design that requires certain resources not needed anywhere else. But that’s not enough – you need to know when the user will these resources so you don’t go preloading everything just in case. With search it’s when they focus inside a search box – they start typing the query, while you start preloading the resources in the background.

Other obvious places to preload resources:

  • My account – when users click log-in or focus into the login form
  • Checkout – when users add stuff to their cart

Other less obvious places are landing pages when the choice a user makes turns into many sub-options, especially when products have the same resources used on the content pages.

To the code!

If you’re already using a library it probably provides AJAX methods and event methods. If not, you can search MicroJS to find one and adapt the syntax.

A simple preloader is almost as simple as helloWorld – the only thing you need to make sure is that the type of data type is set to text so that it does not get executed.

window.preload = function (src) {
	$.get(src, null, null, 'text');
} // call with preload('somefile.css');

If you want to allow the loading of multiple files at once you can detect if the passed element is an array.

window.preload = function (data) {
	var a = $.isArray(data) ? data : [data];
	$.each(a, function (i, n) {
		$.get(n, null, null, 'text');
	});
}
// call with preload('somefile.css');
// or preload(['somefile.css', 'otherfile.js']);

If you prefer to call the function with many parameters you can just use arguments in the each function call.

window.preload = function () {
	$.each(arguments, function (i, n) {
		$.get(n, null, null, 'text');
	});
}
// call with preload('somefile.css');
// or preload('somefile.css', 'otherfile.js');

Key element is to load the resources after the window.onload has happened – this means that any resources needed for the page to function properly have been loaded. If you do stuff sooner you might have to wait for other resources like fonts, images, videos. This means you need to know if the onload event happened – if it has, preload immediately, otherwise wait for the event to fire.

(function () {
	var win = window,
		$ = window.jQuery,
		$win = $(win);
	$win.load(function () {
		$win.data('loaded', true);
	});
	win.preload = function (data) {
		var a = $.isArray(data) ? data : [data],
			fn = function (i, n) {
				$.each(a, function () {
					$.get(n, null, null, 'text');
				});
			};
		if ($win.data('loaded')) {
			fn();
		} else {
			$win.load(fn);
		}
	};
}());
// call with preload('somefile.css');
// or preload(['somefile.css', 'otherfile.js']);

As you can see I also did a few other things – wrapped the code into a function to isolate all the variables. I also assigned window.jQuery to a local $ to make it a bit more bulletproof.

Needless to say this script needs to be loaded during the load stage. If you intend to load it afterwards you need to make sure that you properly detect the onload event – if you don’t nothing will get preloaded as it will be waiting for that event to fire.

Taking your webapp offline

Sunday, August 21st, 2011

I’ve recently done a few things that would benefit from having an offline mode. Empowered by Appcache Facts I tried to make the newly published La Vuelta 2011 results page an offline-capable app.

Goal

The goal of this exercise is twofold:

  1. To force caching of resources that slow down the loading of your page.
  2. To actually be able to use the app when offline.

Solution

It’s as simple as writing a cache manifest. It pays to name it [something].manifest, as Apache already serves the correct header with that suffix. What you do is list all the files you want cached, add stuff you allow on the network (check Appcache Facts and Dive Into HTML5 – Offline Web Applications) and it all works. Except it doesn’t.

CACHE MANIFEST
# version 1

CACHE:
resource.js

NETWORK:
*
http://*
https://*

AJAX stuff

If you’re using AJAX to get data from a server using jsonp/script, your framework of choice (jQuery in my case) will probably default to using a no-cache approach to loading it. This will mean that it will request the file + some suffix to prevent browser caching. This will mean that the resource will not be available if you’re actually online.

You can use navigator.onLine to switch cache on/off, but I suggest you first try requesting the no-cache resource and if it errors out, request the cached resource. The benefit is that even if you are online but the server is not, the users will still see the data / use the app.

$.ajax({
	dataType: 'script',
	url: 'resource.js',
	cache: false,
	success: successHandler,
	error: function () {
		$.ajax({
			dataType: 'script',
			url: 'resource.js',
			cache: true,
			success: successHandler
		});
	}
});

iPad issues

Fixing the AJAX meant that it worked properly on the desktop (test with Firefox that has offline mode) and in Safari on the iPad. The trouble started when I added the app to Home Screen – data failed to load. It was the same app as in Safari and it should have worked.

After some debugging I found out that the data actually was loaded but the eval failed (I was using $.getScript). Some weird testing showed that the problem was a newline character in the data. As I really liked the newline there I added some code to the error handling that removed the newline and evaled the data, then ran success. And it worked!

$.ajax({
	dataType: 'script',
	url: 'resource.js',
	cache: false,
	success: successHandler,
	error: function () {
		$.ajax({
			dataType: 'script',
			url: 'resource.js',
			cache: true,
			success: successHandler,
			error: function (xhr) {
				eval(xhr.textResponse.replace(/\n/g, ''));
				successHandler();
			}
		});
	}
});

Debugging

It’s somewhat hard to debug offline stuff. I suggest using a clearly visible version indicator in the file to make sure you know which version of the file you’re looking at. Also remember that the first time you load the app after changing the file & cache manifest it is served from cache. At the same time the manifest is checked and on the iPad the files are downloaded after everything else is done (app finished loading and rendering).

It works!

After these problems were solved I used the aforementioned navigator.onLine to hide stuff that normally comes from the network but is not relevant to the offline app (banners, share links, like/+1 buttons) and you can now check the La Vuelta 2011 results page.

Form protection

Friday, August 5th, 2011

I’ve seen a discussion recently on how to protect your forms from spammers/bots that come and fill the forms to either fill your database with crap data or fill your page with porn links. When I read the answers I figured out that none of the people read the amazing article I did years ago, so I decided to try to remember what it said. So, a big fat disclaimer: I read this in an article somewhere and I don’t remember where. If you know the original article please post it in the comments, I’d love to link to it, I bet it has way more info than this one.

The problem

Almost all websites now have some forms on them, some of them are contact / registration forms, others use the data submitted and display it on the site itself (comment forms). But letting others submit data to your site/database opens you to all sorts of attacks. If you actually show the content of the submitted form, you’ll get a bunch of spammers posting comments with lots of links. If you only store data and not show it anywhere you’re still at risk – if you don’t notice your disk can fill up, your database may grow beyond its limits,… So what we want to do is to prevent bogus form posting.

Spammer approach

If you think about writing a spam-bot that will try to spam as many sites you possibly can you have two basic approaches.

Record / replay

This is a very simple approach – you use a person to submit the form, preferably with something that looks like real input and record the request made. Then you hand that data off to the bot, it changes some content and tries to resubmit it.

Automation based on heuristics

I wanted to say AI, but it really isn’t. What it is is a set of simple rules and randomized values that the bot thinks might trick your site into accepting the submit. Let’s say you have three fields, two are inputs with field names “name” and “email” and the third field is “comment”. A simple script can fill these with “valid” data and try to submit it.

Human entry

By far the simplest, but also most costly for spammers. Go on Amazon Turk or whatever other service, send a link to a Google Spreadsheet and have people manually enter the stuff into your forms. This is the source of “Sorry, this is my job” endings to spam comments.

Popular solutions

Turing test

Add a field to the form that the user must fill with something that humans can do easily, but machines can’t. The biggest group here are Captchas (display an image with distorted characters, possibly an audio link that reads out the same characters, and have the user somehow figure it out and write the solution), but there have been others, like a “Human?” checkbox, or “3 + 4 =” fields, “Is this a kitten?” with a pic next to it.

2-step process

Supposedly by far the easiest way to do this is by introducing a 2-step process. After the initial submit, you get back a preview of the submitted data, possibly with a checkbox that says “Is this correct?” and another submit button. Robots are usually not good at following through and thus don’t actually submit the content.

Both solutions have an impact on user experience. With Captchas it’s sometimes really hard to see what they are and even if they have a “different image” link, it just seems like the owner of the site wants to make your life hell before you can submit your data. The other challenges might be easier on the user, but also easier to figure out if you’re being targeted by bots. The 2-step process works great for comments, that usually don’t have an edit link, so it might actually be good for user experience if done right (not Wikipedia style), but are less appropriate on other types of forms.

Protect yourself differently

These are the techniques that should prevent most bogus form entries from random passing bots, except “Human entry” – no protection for that, even though Captchas try hard. There is not much you can do when you’re targeted…

Honeypot field

Use this field to trick autoguessing bots to submit something in a field you know should be empty.

  • Add an input field to your form with a regular name (state, maiden-name,…) that does not appear on your form otherwise.
  • Use a label that will clearly communicate that it needs to be empty.
  • Hide it with CSS, preferably not by adding class=”hidden”.

If the form post includes content in this field discard it and redirect back to the form. The trick is to make sure the bots don’t figure out this is a honeypot, so use valid looking but nonsensical classes…

Date field

Use it to prevent resubmit of data too far from the creation date. Allow users a few hours to post the form.

To prevent manual modification you can use either proper encryption (symetric or asymetric) that will allow you to decode it on form post or use this date in combination with the onetime token.

Onetime token

Use this field to prevent replay of request data. If you can, save it into the database.It is a good idea to make this token in a form that it cannot be faked (say one character changed ad you have a valid one). This can be done with hashing data or encryption.

This one can be as tricky as you want. What I usually do (disclaimer: I don’t know much about encryption so this might be crap advice) is use a plain datetime field with the onetime token generated from IP address, UserAgent and the date field with HMAC. There is no need for this token to be reversible – I can recreate the same thing with the data from the form post and check if it matches.

When using these techniques make sure you take care of the user experience. If you detect a problem on what might be valid user input (“timeout” on the date field with a non used onetime token, wrong onetime token from an ip change by the service provider), you might want to display a second step from the “2-step process”. Whatever you do, don’t call your users spammers or bots – be nice, bots don’t read the text anyway.

Did I miss anything?

I know of no plugin that uses all of these techniques, but I haven’t really looked for it. What I do know is that I don’t want to ever use a Captcha, cause it often keeps me out, and the 2-step process in just too weird sometimes. Hope this helps. And again – if you find the original article (must be some 5 years old now at least if not more) or have any other solutions you use or endorse, do leave a comment.

View Source Alliance

Friday, November 19th, 2010

Most of what I learned on the web in my early years was from “View Source”. Then came the books and the conferences.

It makes me sad to see lots of sites minifying code for performance and not releasing the full version of the code so other developers could learn from it. It’s the openness that I really like about the web.

I think there should be a “View Source Alliance” that would set rules on how to release your code in a way that visitors can benefit from the speed of minified code, while web developers can still find your full files and learn from them.

I’ll set a few simple rules here, hoping somebody with more reach picks them up:

  1. If you minify the files for them, use a simple convention name.min.ext (say jquery.min.js)
  2. When you deploy minified files, also deploy their full version at name.ext (say jquery.js)
  3. If you for some reason can’t release the full files next to the minified ones, add this to the top of the minified file: /*viewsource*http://path.to/full.ext*/ (say /*viewsource*http://http://code.jquery.com/jquery-1.4.4.js*/)

This way you will not only help others, but sometimes even stop breaking the law. Because you might be using some open source code with a licence that says you must release your code with a same/similar licence.

The Cross-Origin Request Sharing (CORS) knot

Tuesday, August 3rd, 2010

As a developer for a company that does a lot of cross-domain posting I was happy to see CORS finally happening. I’ve authored jQuery.windowName plug-in to add support for cross-domain posting in the past and this was my way out – no more hacks as browser support for CORS grows.

Yeah right.

CORS is supported on XMLHttpRequest object in Gecko and Webkit. You just request something on a different domain, and if the response has the right header with the right value you get the data. In IE you have to use a new object called XDomainRequest which is somewhat similar to XMLHttpRequest, but without readystate, onreadystate event handler and basically any header getters/setters.

Sounds ok and when I first tried it I was quite happy with how it worked. But unfortunately nothing is that simple in the land of browsers. When it fails, it’s a whole new game…

Gecko (Firefox)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}

Webkit (Safari, Chrome)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ”}
  • 304 response: xhr = {status: 0, statusText: ”}

IE

Same domain requests made with XMLHttpRequest, cross domain requests made with XDomainRequest.

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: onload invoked
  • 304 response: onload with cache, onerror otherwise
Cross domain request without proper headers:
  • 200 response: onerror invoked
  • 304 response: onerror invoked

Where’s the knot?

The problem with this is the lack of error handling. Firefox will return status 0 on all errors, but will indicate what the response was with statusText. It will also return 0 on a 304 response, which is a bit weird. Webkit on the other hand only returns status 0 on failed requests, but without telling you what’s wrong. You get the same error when a preflight request fails, a 500 error on the server or the real request fails. IE with its own separate XDomainRequest implementation simplifies this two handlers – onload and onerror. Unfortunately it raises an onerror request when a 304 is returned on empty cache.

jQuery issues

If you’re working with jQuery (and possibly other libraries) you’re in for a treat. Opera 9.5 falsely reports status = 0 when a HTTP status 304 is returned from the server. For this reason jQuery treats status === 0 as a successful request. Thus you have no way of knowing whether your request was a success or a failure.

Untying the knot

If you intend to use jQuery’s built-in methods you’ll need to do your own bookkeeping:

  1. Know when a request is a CORS request
  2. Wait for complete event
  3. If data is empty trigger error handling

For this to work you obviously always need some data. If empty data is valid you’re screwed.

Easy way out

You might have noticed before that I made a jQuery.windowName plugin. It still has the same name, but now also supports CORS requests and does some of the CORS related bookkeeping. As the latest version is still in testing you can get it directly at http://friedcell.net/js/jQuery.windowName/jQuery.windowName.plugin.1.0.0.js (test page).

Enhanced by Zemanta