About jkneb

Freelance web & print designer / front-end developer living and coding in Paris, France. Passionnate about CSS3, HTML5 and JavaScript, big fan of Sass, TextMate2 and MacVim. | Currently enjoying EmberJS + Handlebars :) | Folio : julienknebel.com | Blog : front-back.com | Twitter : @julienknebel | Dribbble:

Enlever le layer noir au click event sur les appareils iOS

Sur iPad ou iPhone quand on click sur quoi que ce soit, on voit apparaître pendant environ une seconde un layer noir transparent par dessus l'élément html en question. C'est plutôt moche et ça donne l'impression de ralentir l'expérience utilisateur.

Pour s'en débarrasser il vous suffit d'appliquer cette propriété css sur les éléments qui vous intéressent :

a { -webkit-tap-highlight-color: rgba(0,0,0,0); }

En gros ça dit que la couleur de -webkit-tap-highlight-color sur tous les <a> est noire (les 3 premiers zéros dans rgba) et que son opacité est à zéro. Continue reading ...

SASS et LESS : mixin pour multiple shadow

J'ai posté une astuce il y a peu pour rendre mon mixin SASS box-shadow compatible avec plusieurs shadows dans la même déclaration. Je me suis retrouvé avec un truc assez moche et surtout très redondant.

Sans plus attendre...

Mixin multiple box-shadow v2

@mixin box-shadow($params) {
  -webkit-box-shadow : #{$params};
     -moz-box-shadow : #{$params};
          box-shadow : #{$params};
}

Voilà comment ça s'utilise :

@include box-shadow( "0 0 5px #333 , inset 0 0 2px #666" );

Et c'est tout ! :D

En fait le principe est de passer une STRING en paramètre (donc concrètement tout ce que vous voulez) puis dans le code du mixin on se sert des #{ } pour échapper les guillemets.

Continue reading ...

Ras le bol de devoir return false sur les balises link au click event ?

Si vous aussi vous en avez marre de devoir spécifier (ou carrément d'oublier de spécifier) les return false sur les balises <a href="#"></a> pour éviter que l'internaute soit violemment expulsé en haut de la page quand il clique sur le lien en question, eh bien sachez qu'une lecture rapide de la doc du W3C concernant les balises link vous aurait appris que l'attribut href est optionnel !

Citation W3C :
"Authors may also create an A element that specifies no anchors, i.e., that doesn't specify href, name, or id. Values for these attributes may be set at a later time through scripts."
++

Traduction :
"Les auteurs peuvent aussi créer un <a> qui ne spécifie pas d'ancre, par exemple, qui ne spécifie ni href, ni name ou id. Les valeurs pour ces attributs peuvent être set up plus tard via des scripts."

Il vous est donc possible d'écrire vos liens comme ceci dans vos pages :

# html
<a id="bob" class="leponge">click me!</a>

# js
$('#bob').on('click', function(){
    noNeedToReturnFalseYou(ahahah);
});

# css
a { cursor:pointer; }

Et voilà plus besoin d'annuler le comportement habituel du lien en javascript au moment du click.

SASS : un mixin box-shadow qui supporte plusieurs shadows

Mon mixin box-shadow me rend bien des services, il permet d'écrire :

UPDATE : j'ai trouvé beaucoup plus simple que la technique décrite ici.

@include box-shadow(0px 0px 5px #666);

Qui est ensuite compilé en ça :

-webkit-box-shadow: 0px 0px 5px #666;
   -moz-box-shadow: 0px 0px 5px #666;
        box-shadow: 0px 0px 5px #666;

Le code SCSS de mon mixin en question :

@mixin box-shadow($params) {
  -webkit-box-shadow: $params;
     -moz-box-shadow: $params;
          box-shadow: $params;
}

Malheureusement il ne marche pas si on essaye de lui passer plusieurs shadows pour la même déclaration. Un truc comme ça quoi :

box-shadow: 0px 0px 10px #666,
            0px 0px 15px #666,
            0px 0px 20px #666,
            etc...

Pas moyen d'y arriver avec mon mixin tel quel...

Donc après quelques recherches du côté de Compass puis du côté de StackOverflow je l'ai modifié pour lui permettre de supporter plusieurs shadows dans la même déclaration (9 shadows maximum pour être plus exacte, on va dire que ça suffira hein) :P

Et voilà, mon petit mixin tout mignon du début s'est transformé en cette chose énorme, immonde et illisible :

@mixin box-shadow( $shadow1, $shadow2:false, $shadow3:false, $shadow4:false, $shadow5:false, $shadow6:false, $shadow7:false, $shadow8:false, $shadow9:false ) {
$params: $shadow1;
@if $shadow2 { $params: $shadow1, $shadow2; }
@if $shadow3 != false { $params: $shadow1, $shadow2, $shadow3; }
@if $shadow4 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4; }
@if $shadow5 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4, $shadow5; }
@if $shadow6 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4, $shadow5, $shadow6; }
@if $shadow7 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4, $shadow5, $shadow6, $shadow7}
@if $shadow8 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4, $shadow5, $shadow6, $shadow7, $shadow8 }
@if $shadow9 != false { $params: $shadow1, $shadow2, $shadow3, $shadow4, $shadow5, $shadow6, $shadow7, $shadow8, $shadow9 }

-webkit-box-shadow: $params;
   -moz-box-shadow: $params;
        box-shadow: $params;
}

Et ça s'utilise comme ça :

@include box-shadow( 0px 0px 5px blue, 0px 0px 5px lightBlue, etc. );

Donc 9 shadows max. Mais on peut toujours paramétrer le mixin pour qu'il en accepte plus.

Je vais quand même chercher un moyen de simplifier tout ça, peut-être avec @each je sais pas mais ça vaudrait le coup. Enfin bon au moins maintenant il supporte le multiple shadows ! :D

UPDATE : j'ai trouvé beaucoup plus simple que la technique décrite ici.

Faire déborder des éléments en dehors d’un parent en overflow hidden

Quel intégrateur HTML n'a pas un jour rencontré le cas de figure où il a besoin de faire déborder un élément par rapport à son parent et là, aïe... le parent en question est en overflow:hidden... Eh oui cette propriété fonctionne comme un masque qui cache tout ce qui dépasse (on s'en sert aussi beaucoup pour résoudre le fameux problème de float). Voie sans issue donc ? Non voilà une astuce :) Prenons un exemple d'une div parente en overflow:hidden qui contient une div enfant en position:absolute; et on la décale de manière à essayer de la faire déborder.

L'HTML

<div class="parent">
  <div class="enfant"></div>
</div>

Le CSS

.parent {
  position:relative;
  overflow:hidden;
  margin:0 auto;
  height:100px;
  box-shadow:inset 0px 0px 12px #555;
  background-color:white;
}
.enfant {
  position:absolute;
  top:-10px;
  left:-5px;
  width:30px;
  height:30px;
  background-color:rgba(30,110,190,.8);
}

Le rendu

Bon donc oui on se rend bien compte que ça ne marche pas. Le petit carré bleu est mangé dans l'overflow hidden de son parent.

La solution

Le trick se situe dans le fait de NE PAS définir de propriété "position" (absolute / static / relative / fixed) sur la div qui possède l'overflow hidden (juste ne rien mettre) et là... magie le contenu en absolute apparaîtra en dehors de l'overflow.

L'HTML

<div class="fakeParent">
  <div class="parent">
    <div class="enfant"></div>
  </div>
</div>

Le CSS

.fakeParent {
  position:relative;
}
.parent {
  /* position:relative; */
  overflow:hidden;
  margin:0 auto;
  height:100px;
  box-shadow:inset 0px 0px 12px #555;
  background-color:white;
}
.enfant {
  position:absolute;
  top:-10px;
  left:-5px;
  width:30px;
  height:30px;
  background-color:rgba(30,110,190,.8);
}

Le rendu

 

Attention justement à cet absolute qui du coup va remonter le DOM jusqu'à trouver un parent pour le contenir (en l'occurrence un parent en relative OU en absolute). Donc il est judicieux d'encapsuler le tout dans une nouvelle div parente sur laquelle on met simplement le position:relative. Et voilà, le tour est joué !

Simple hein ? Evidemment on est pas obligé de CRÉER une nouvelle div spécialement pour ça. On pourrait utiliser une div parente déjà existante dans la page et qui pourrait faire l'affaire. Mais bon ce n'est pas toujours possible donc on rajoute juste un parent supplémentaire (l'exemple de notre fakeParent) ce qui aura finalement peu d'impacte en terme de performance sur notre application / site web.

Ressources

Article de Thierry Koblentz en anglais énumérant les différentes manières de « clearer les float » et démontrant l'utilisation de la technique évoquée dans ci-dessus :
L'article lui même
Page de démo

Mieux organiser ses fichiers CSS avec SASS

Déjà SASS c'est quoi ?

Alors SASS ou bien SCSS est un langage qui permet d'améliorer nos CSS traditionnels. C'est basé sur Ruby et ça s'installe sur l'ordinateur en tant qu'exécutable. Vous écrivez votre CSS de manière plus sympa et ensuite ça compile ce que vous avez écrit en une feuille de style CSS tout ce qu'il y a de plus classique. Et c'est cette feuille de style que votre page web appelle. Cette étape de compilation peut soit être faite à la main ou bien peut se faire tout seule chaque fois que vous faite une modification. SASS a un concurrent direct qui s'appelle LESS et qui fait sensiblement la même chose avec quelque légères variantes syntaxiques.

Un petit tour du propriétaire

Voilà le topo. Aujourd'hui ce qui se fait le plus couramment c'est d'écrire tous les styles dans un seul gros fichier CSS qui s'appelle presque à coup sûr style.css.

<head>
  <link rel="stylesheet" href="css/style.css">
</head>

Le plus gros problème avec cette manière de faire concerne l'administration de style.css sur le long terme. En effet avec un fichier qui fera entre 1000 et x millier de lignes, bah au bout d'un moment plus personne sait où on en est. Donc là une manière simple de faire serait de fragmenter style.css en plusieurs fichiers distincts. Comme ceci par exemple :

<head>
  <link rel="stylesheet" href="css/grid.css">
  <link rel="stylesheet" href="css/header.css">
  <link rel="stylesheet" href="css/footer.css">
  <link rel="stylesheet" href="css/lightbox.css">
  <link rel="stylesheet" href="css/tooltip.css">
  <link rel="stylesheet" href="css/ce-que-vous-voulez.css">
</head>

Ici c'est effectivement plus clair dès le premier coup d'oeil mais on trouve deux inconvénients majeurs à cette manière de faire. Premièrement il faut sans cesse maintenir à jour les appels css du <head>, ce que je déteste faire parce que je suis un gros feignant :P et deuxièmement ce n'est pas ce qu'il y a de mieux pour les performances de chargement. Simplement parce que chaque requête HTTP évitée est un gain généralement non négligeable. L'exemple le plus flagrant c'est l'iPhone et l'iPad qui aujourd'hui ne sont pas capables de paralélliser les requêtes. Ceux-ci chargent chaque fichier CSS (ou autres) un par un... Pas top du tout donc. Bon sinon on pourrait peut-être tirer parti de la propriété css @import... Cette propriété permet d'inclure un fichier css dans un autre. Déjà premier avantage : ça permet de revenir à un fichier unique qui est chargé dans la page.

<head>
  <link rel="stylesheet" href="css/style.css">
</head>

Et maintenant dans style.css, voilà à quoi ça ressemblerait :

/* je suis un fichier CSS */
@import "css/grid.css";
@import "css/header.css";
@import "css/footer.css";
@import "css/lightbox.css";
@import "css/tooltip.css";
@import "css/ce-que-vous-voulez.css";
...

Ah là c'est pas mal ! C'est propre on a plusieurs CSS qui sont appelés à l'intérieur de style.css et du coup style.css lui est le seul fichier appelé dans la page donc une seule requête HTTP pour la page, pas mal.

Mais là encore malheureusement c'était sans compter sur cet @import et de son interprétation dans nos navigateurs... En effet cette technique empêche nos navigateurs de paralélliser les requêtes HTTP et sont donc obligés de charger les différents fichiers CSS un par un... Encore pire que la méthode précédente.

Bon et donc comment SASS pourrait-il m'aider à mieux organiser mes CSS ?

Et bien tout simplement en utilisant à nouveau le @import mais cette fois-ci celui de SASS, PAS CELUI de CSS. En fait SASS (et LESS aussi d'ailleurs) permet lui aussi d'inclure des fichiers sass les uns dans les autres. Sauf que ça se fait côté environnement de développement et non plus côté navigateur au moment de l'affichage de la page. En fait voilà comment ça se présenterait dans le navigateur :

<head>
  <link rel="stylesheet" href="css/style.css">
</head>

Donc un seul fichier CSS. Cool ! Et comme on est en SASS en fait on ne travaille pas directement dans ce fichier style.css on travail dans sa version SCSS (ou SASS) c'est à dire qu'on travaille dans style.SCSS qui est ensuite transformé en style.CSS.

Donc depuis le fichier SCSS on peut importer d'autres fichiers SCSS !

// je suis un fichier SCSS/SASS
@import "scss/variables.scss";
@import "scss/mixins.scss";
@import "scss/grid.scss";
@import "scss/header.scss";
@import "scss/footer.scss";
@import "scss/lightbox.scss";
@import "scss/tooltip.scss";
...

Et là on tire profit à la fois de la performance dans le navigateur grâce à un seul fichier CSS et également de la clarté du code grâce à la fragmentation en plusieurs fichiers distincts.

Pas mal hein ? :)

Remarque importante

Vous noterez au passage les deux premiers @import : variables.scss et mixins.scss. En les important en tout premier on peut en faire profiter tous les fichiers SCSS suivants. Exemple : on a un mixin qui s'appelle border-radius(); qui est définit dans mixins.scss, et on peut l'utiliser sans soucis dans tooltip.scss qui lui est importé en dernier. Idem pour les variables ! C'est juste génial on a du coup même pas besoin de faire des @import "scss/variable.scss" et @import "scss/mixins.scss" en haut de chacun des autres fichiers !

Encore mieux hein ? :P

Ressources

• Le site de SASS et celui de LESS
• Article Alsacréations : N'utilisez pas @import
Don't use @import css rule
• Mon propre mini boilerplate qui utilise cette technique (avec des vrais morceaux de Twitter Bootstrap dedans)

Un preloader sympa en full CSS3 avec LESS

Aujourd'hui la triste réalité des preloaders sur le web fait qu'en fait ils ne servent à rien. À part à égailler l'expérience utilisateur de l'internaute. Ils ne servent à rien parce qu'il est techniquement impossible "d'écouter" la progression du chargement de quelque chose dans une page web (hormis les sites en flash qui eux sont capables d'afficher des loader qui fonctionnent vraiment). Bon donc concrètement on voit plein de petits loader sympas qui fleurissent sur la toile mais en fait ce sont tous des gif animés... Pourquoi donc des gif animés ? Parce qu'ils sont faciles à trouver (ajaxload.info), faciles à implémenter (en général le gif est positionné en background-image avec un positionnement center center), et en plus ils sont légers.

Bon, je me suis amusé à m'en faire un en full css3 + css transitions 2D donc pas d'image, que des propriétés css. La compatibilité navigateurs est pas top mais pas non plus dégueu et puis c'était sympa à faire :P

La démo

 

Le code HTML

<div class="loader rspin">
  <span class="c"></span>
  <span class="d spin"><span class="e"></span></span>
  <span class="r r1"></span>
  <span class="r r2"></span>
  <span class="r r3"></span>
  <span class="r r4"></span>
</div>

Le code CSS

Le css qui en découle est complexe et peu facilement administrable il est donc plus intelligent de le faire en LESS ou en SASS pour profiter des variables au sein du fichier css.

@loaderSize       : 55px;
@radThickness     : @loaderSize/10;
@spinSpeed        : 750ms;
@backgroundColor  : #eee;
@forgroundColor   : #ccc;
@masksColor       : #fff;
@middleCircleSize : 68%;

Dans mon présent exemple j'ai donc choisi d'utiliser LESS et j'ai fais en sorte de rassembler les variables en haut du fichier. De là on peut gérer tous les paramètres importants du spinner, sa taille, ses couleurs, sa vitesse de rotation, etc. Exemple : modifiez @loaderSize pour redimensionner la totalité du loader et des éléments qui le composent (tout en conservant leurs proportions). Ensuite il ne reste plus qu'à compiler vers du CSS et le tour est joué.

Avantages

• Fonctionne sur tous les navigateurs modernes et sur les appareils mobiles.
• Pas de requête HTTP pour charger une image dans la page.
• L'animation est en css3 donc elle est légère et fluide car non-basée sur JavaScript.
• Si on zoom sur le loader (essentiellement dans le cas où l'on surf depuis une tablette) le loader reste parfait, contrairement à une image qui paraîtra toute moche et étirée.
• Taille personnalisable si utilisé avec LESS.
• Permet de frimer en soirée geek :)

Inconvénients

• Plus compliqué à administrer et à mettre en place qu'une simple image en background.
• Pas de compatibilité sur IE9 et en dessous (à l'heure où j'écris cet article il y a de bonnes chances que ça marche sur ie10).
• Rajoute du markup HTML dans la page.

Vas-y Git, mets du stash !

Git est un extraordinaire outil en ce qui concerne le versionning de fichiers. J'ai testé ses deux autres concurrents, à savoir SVN et Mercurial et je dois dire que je suis toujours autant in love de Git. Pour info le fonctionnement de Mercurial est quasi-identique à celui de Git mais je sais pas je préfère Git. Et puis ce dernier jouit d'une immense communauté sur le net il est donc plus facile de trouver de la doc et des tutos (et puis y a github).

Je voudrais parler de Git stash parce que cette commande est juste super sexy. Elle permet 1) de se la péter en soirée geek et 2) éventuellement de vous sauver la vie, parole de geek.

Git stash

En fait ça sert à mettre rapidement de côté des modifs en cours et de les récupérer tout aussi rapidement. Généralement c'est très pratique pour réaliser une/des manip(s) qui pourrai(en)t entrer en conflit avec nos fichiers qui justement sont trop frais pour être commités.

Prenons un exemple, j'ai fais des modifs genre méga top secrètes et je fais un git status pour voir où j'en suis.

[bob@bob (master =)]$ git status
# On branch master
# Changes not staged for commit:
#
#	modified:   css/style.css
#
[bob@bob (master *=)]$

Bon soyons clair la modif dont on est en train de parler ne peut pas être commit telle quel. Pas question que les devs voient ça, c'est trop... fin, trop... précieux, c'est surtout trop tôt pour que des gros doigts boudinés de dev viennent détruire à jamais sa candide pureté ;) Et là... it's the drame, un des devs vient de pousser un truc... et il me dit qu'il arrive pour en parler... omagad omagad ! Qu'est-ce que je vais faire ? Je peux pas commit et il sera là avec moi derrière l'écran dans environ 5 secondes et pendant la discussion il va forcément y avoir un diff qui révélera mon code si secret... alors bon là c'est le moment ou je commence à transpirer, mon coeur s'emballe, mes mains deviennent moites et l'angoisse prend possession de mon frêle petit être... et... en fait c'est la fin, tout est terminé. Les ténèbres m'ont englouti et je suis à présent en train de dériver au dessus de mon corps inanimé, sans vie. Dur hein ?

Bon ! Si j'avais connu git stash l'histoire ne se serait pas terminée ainsi. En fait je n'aurais même pas scillé, 1) j'aurais tapé git stash dans mon shell et 2) à nouveau un git status pour voir ce que ça aurait donné.

[bob@bob (master *=)]$ git stash
# Saved working directory and index state WIP on master: b6e4b45 message du dernier commit
# HEAD is now at b6e4b45 message du dernier commit
[bob@bob (master *$=)]$ git status
# On branch master
# nothing to commit (working directory clean)
[bob@bob (master *$=)]$

Et tadaaa ! le fichier modifié n'est plus là. Mais il n'a pas du tout disparu, faisons un git stash list.

[bob@bob (master *$=)]$ git stash list
stash@{0}: WIP on master: b6e4b45 message du dernier commit
[bob@bob (master *$=)]$

La modif est dans ce truc bizarre là : stash@{0} et WIP ça veut dire Work In Progress. On pourrait même avoir plusieurs stash si on les cumulait. Ça donnerait ça :

[bob@bob (master *$=)]$ git stash list
stash@{0}: On master: et ainsi de suite
stash@{1}: On master: une autre modif top secrete
stash@{2}: WIP on master: b6e4b45 message du dernier commit
[bob@bob (master *$=)]$

On peut aussi se laisser un petit message si on veut, en tapant :
git stash save "mon super message bien pratique"
Ça permet d'y voir plus clair si par exemple on a beaucoup de modifs stashées.

Bon très bien et maintenant récupérons notre modif avec git stash pop pour continuer son secret développement ;)

[bob@bob (master *$=)]$ git stash pop
# On branch master
# Changes not staged for commit:
#
#	modified:   css/style.css
#
[bob@bob (master *=)]$

Par défaut git stash pop (ou git stash apply) vous récupérera le dernier stash en date (le plus récent pas le plus vieux). Et si on voulait en récupérer un particulièrement dans la liste ?

Eh bien là on devra faire un git stash pop leNomChelouAvecDes{} et comme c'est un nom imbitable vous pouvez utiliser la touche TAB pour démarrer l'autocompletion ça vous simplifier un peu la saisie.

Donc ça donnerait un truc comme ça :

[bob@bob (master *$=)]$ git stash pop stash@{2}
# On branch master
# Changes not staged for commit:
#
#	modified:   css/style.css
#
[bob@bob (master *$=)]$

Pas mal hein ?

Liste des principales commandes

git stash
# ou
git stash save "message"

(pour stasher les modifs en cours)

git stash -u
# ou
git stash save -u "message"

(pour stasher AUSSI les fichier non trackés, par défaut git stash ne les prends pas en compte)

git stash pop
# ou
git stash apply

(pour récupérer les dernière modifs stashées)

git stash pop stash@{2}

(pour récupérer un stash bien précis dans la liste)

git stash clear

(permet de supprimer toute la liste des stash)

git stash drop stash@{2}

(permet de supprimer un stash particulier dans la liste)

git stash show stash@{2}

(permet d'avoir plus d'infos sur le contenu d'un stash de la liste)

git stash -h

(pour afficher l'aide des commandes)

Conclusion

Git stash sert AVANT-TOUT à cacher rapidement du code qu'on veut garder secret :D Bon trêve de plaisanteries, vous l'aurez compris c'est bien pratique quand on veut rapidement faire le ménage dans un projet sans perdre les modifs en question.

Bon allez stashez bien ! Bande de stash...

Détecter les différentes versions d’iPhone et d’iPad en JavaScript

Je travaille en ce moment sur une web app (html5, css3, terminaux mobiles, media queries et compagnie) et je cherchais un test simple pour détecter si on se trouve sur un iPad 3. Ne trouvant pas mon bonheur du côté de Modernizr, j'ai finit par tomber sur deux choses qui à mon sens sont suffisamment précises pour obtenir un test fiable. Et oui je parle bien de tester le user agent...

Bouuu trop la loose de tester le user agent. Ouais mais bon ça a le mérite d'être ultra rapide à écrire :P et puis faute de mieux c'est ce que je vais utiliser.

Habituellement on teste la totalité de la chaîne de caractères (le user agent donc) avec une regex et ça donne un truc comme ça :

if (navigator.userAgent.match(/iPad/i)) { /*doSomethingAwesome...*/ }

C'est simple rapide et concis mais on peut pas cibler un iPad 3 versus un iPad 2 par exemple. En fait on peut affiner le test pour être du coup quasi sûr de ne pas se tromper.

1. window.pixelRatio

Le pixelRatio aura la valeur "2" pour les iOS avec écran Rétina, c'est-à-dire les écrans HD d'Apple actuellement équipés sur les iPhone 4, 4S et iPad 3. Et il aura la valeur "1" notamment pour les iPhone 3GS, les iPad 1 et 2.

2. navigator.platform

Contrairement à navigator.userAgent (qui renvoi une chaîne interminable) eh bien lui il ne renvoi qu'une toute partie du user agent, en l'occurrence seulement ce qui concerne la plateforme. Et il se trouve que pour l'iPad le navigator.platform c'est "iPad" :) cool non ? Plus rapide que de tester TOUTE la chaîne avec une regex.

Et donc j'en suis arrivé à ce petit bout de js, très moche certes mais on s'en fiche, sous cette forme il a un côté facile à appréhender.

if (window.devicePixelRatio === 2) {
  if (navigator.platform === "iPad") {
    alert('iPad 3');
  } else if (navigator.platform === 'iPhone') {
    alert("iPhone 4");
  };
} else {
  if (navigator.platform === 'iPad') {
    alert('iPad 2');
  } else if (navigator.platform === 'iPhone') {
    alert('iPhone 3');
  }
};

Après on prend que certaines parties ou bien on l'adapte à ses besoins selon l'humeur.

Ressources

Modernizr : pour faire de la feature detection plutôt que du test de user agent
HTML5 Mobile Boilerplate : pleins de ressources pour faire de la web app
Alex Gibson demo page : pour utiliser un splash screen comme dans les apps natives