Javascript ( 20 / 21 articles - Voir la liste )

Marque-page Marque-pages React

Contrairement à Angular, React propose peu de solutions par défaut aux problèmes les plus courants. Il faut souvent ajouter des modules en dépendance, et il est parfois difficile de ne pas se perdre dans la jungle des modules pour React.

Voici une liste de modules et de pratiques utiles pour les besoins courants.

Socle

Mise en place

On peut initialiser facilement un projet React avec l’outil CRA (Create React App). Il crée l’arborescence de base, via une simple commande comme npx create-react-app my-app --template typescript.

Routage

Le module react-router fait ça très bien.

Appels API

Pour récupérer des données auprès d’une API ou via d’autres méthodes asynchrones, on peut utiliser SWR ou react-query.

État

Si on souhaite gérer un état globale de l’application au niveau local, on peut utiliser redux avec redux-toolkit ou alors recoil.

Formulaires

Le module react-hook-form fait ça très bien. Il supporte notamment Yup pour gérer la validation de champs.

Internationalisation

Le module react-i18next permet de gérer une appli multilingue.

Design

CSS

Pour restreindre des classes CSS à un composant en particulier, on peut utiliser le concept de modules CSS.

Material

Les principales bibliothèques graphiques sont disponibles pour React, et notamment Material UI. Elle fonctionne très bien, sauf avec le hook-form. C’est problématique et cela nécessite de recréer un composant « Contrôlable », pour chaque type de champ de formulaire.

Qualité

Tests

Plusieurs bibliothèques pour les tests fonctionnent bien avec React, notamment jest.

Lint

On peut combiner prettier et eslint, pour normaliser la base de code. Il existe des extensions à eslint spécialement pour React, afin de détecter et remonter si certaines mauvaises pratiques sont utilisées.

Documentation des composants

Pour documenter et présenter une bibliothèque de composants, on peut utiliser Storybook. Cela permet de manipuler et tester chaque composant, dans une interface dédiée.

Autres

Base de donnée locale

Pour utiliser plus facilement la base de données locale IndexedDB, on peut y accéder via dexie.

Authentification avec JWT

Pour extraire les données d’un jeton JWT, on peut utiliser jwt-decode.

Marque-page Modèle de composant

Voici un modèle de composant React minimal :

// MyComponent.tsx
import React, { ReactNode } from 'react';
import styles from "./MyComponent.module.scss";

interface Props {
  children: ReactNode;
  notRequiredProperty?: string;
}

export const MyComponent: React.FC = ({ children, notRequiredProperty }: Props) => {
  return <div className={styles.myComponentClass}>{children}< /div>;
};

Explications :

  • Le composant est utilisable dans un autre composant, via <MyComponent children={<p>EXAMPLE</p>} />
  • On importe le fichier MyComponent.module.scss, dans lequel se trouve une règle CSS pour la classe myComponentClass.
  • Le composant est un wrapper, auquel on passe un sous-composant via la propriété children.
  • On déclare l’interface Props pour définir les propriétés du composant. Cela facilite l’autocomplétion et permet la validation du typage à la compilation.
  • La propriété notRequiredProperty est facultative lorsqu’on appelle le composant.

Héritage

Si on souhaite créer un wrapper pour un composant existant, on veut souvent qu’il accepte les mêmes propriétés. Dans ce cas, on peut déclarer l’interface et le composant ainsi :

type Props = SomeComponentToWrapProps & {
  extraProperty1: string
};

export const MyComponent: React.FC = ({ extraProperty1, inheritedProp1, ...others }: Props) => {
  // [...]
};

Explications :

  • Les propriétés du composant sont l’union de celles de SomeComponentToWrapProps, avec celle déclarée ici.
  • On indique les quelques propriétés dont on a explicitement besoin, et on peut accéder à toutes les autres via l’objet others.

Marque-page Construire une queryString

Voici une fonction pour construire une queryString à partir d’un objet.

Version JavaScript :

export const buildQueryString = (parameters) => {
  const paramParts = Object.entries(parameters)
    .map(([key, value]) => `${key}=${encodeURI(value)}`);

  return paramParts.length > 0 ? `?${paramParts.join('&')}` : '';
};

Version TypeScript :

export const buildQueryString = (parameters: { [key: string]: string }) => {
  const paramParts = Object.entries(parameters)
    .map(([key, value]) => `${key}=${encodeURI(value)}`);

  return paramParts.length > 0 ? `?${paramParts.join('&')}` : '';
};

Explications :

  • Chaque propriété de l’objet devient une clé de la queryString, dont la valeur échappée.
  • Les paramètres sont séparés par & et ? est ajouté en préfixe de la chaîne.

Astuce Debounce en React

Régulièrement en Javascript, on veut écouter les changements sur un élément, mais ne les prendre en compte qu’à intervalle régulier.
Par exemple, si on écoute le déplacement de la souris pour lancer un traitement, on n’a pas besoin de l’exécuter à chaque changement de position, mais uniquement tous les 300 ou 500ms.

Pour éviter l’effet « rebond », on utilise alors une fonction de debounce, ou dans React, un hook.

Script

import { useEffect, useState } from 'react';

/**
 * Delays the value change, so it’s only committed once during the specified duration.
 *
 * Usage :
 * <pre>
 * const debouncedValue = useDebouncedValue(realTimeValue, 500);
 * <pre>
 */
export const useDebouncedValue = <T>(input: T, duration = 500): T => {
  const [debouncedValue, setDebouncedValue] = useState(input);

  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedValue(input);
    }, duration);

    return () => {
      clearTimeout(timeout);
    };
  }, [input, duration]);

  return debouncedValue;
};

Exemple d’utilisation

On veut afficher un message dans la console au changement de valeur du champ de formulaire,
mais limiter ce traitement avec un debounce.

import React, { useEffect, useState } from 'react';
import { useDebouncedValue } from './utils/debounce';

export default (() => {
  const [myValue, setMyValue] = useState('');
  const myDebouncedValue = useDebouncedValue(myValue, 500);

  useEffect(() => {
    console.log('This log is displayed when the value of debouncedFilterNom changes', myDebouncedValue);
  }, [myDebouncedValue]);

  const handleValueChange = async (event: any) => {
    setMyValue(event.target.value);
  };

  return (
    <form>
      <input type="text" onChange={handleValueChange} value={myValue} />
    </form>
  );
}) as React.FC;

Astuce SessionStorage vs LocalStorage

Dans une application ou un site web, on peut stocker des informations dans le navigateur de l'utilisateur, pour un nom de domaine spécifique.

Deux approches principales : les cookies et le Storage. Pour la seconde, deux possibilités : sessionStorage ou localStorage.

cookie vs Storage

La principale différence, c'est que les cookies sont accessibles côté client et côté serveur. À l'inverse, il n'y a aucun moyen côté serveur d'accéder au contenu ni du localStorage ni du sessionStorage.

Remarque :

Quand on parle de "sessions" pour le sessionStorage, il n'y a aucun rapport avec les sessions entre client/serveur comme on peut les manipuler en PHP, par exemple.

session vs local

Les données stockées en sessionStorage sont rattachées à un onglet.
Si on recharge la page au sein d'un même onglet, les données sont conservées. En revanche, elles expirent à sa fermeture.

Les données stockées en sessionStorage sont rattachées à un onglet.

Remarque :

Selon le navigateur, si on restaure un onglet fermé précédemment, on récupère le sessionStorage tel qu'il était avant fermeture. C'est le comportement pour les navigateurs basés sur Chrome. De même, si on duplique un onglet ouvert, la copie hérite d'une copie du sessionStorage de l'onglet original.

Pour aller plus loin

Erreur Impossible de charger les dépendances depuis https://jcenter.bintray.com

Si vous construisez des APK Android avec Cordova, vous pouvez rencontrer ce genre d'erreur :

[...]
> Task :app:preReleaseBuild FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all files for configuration ':app:releaseRuntimeClasspath'.
> Could not resolve com.android.support:support-v4:27.+.
  Required by:
      project :app
   > Failed to list versions for com.android.support:support-v4.
      > Unable to load Maven meta-data from https://jcenter.bintray.com/com/android/support/support-v4/maven-metadata.xml.
         > Could not get resource 'https://jcenter.bintray.com/com/android/support/support-v4/maven-metadata.xml'.
            > Could not GET 'https://jcenter.bintray.com/com/android/support/support-v4/maven-metadata.xml'.
               > Read timed out
[...]

Cela est dû à la coupure du dépôt jcenter.

La solution pour le remplacer est décrite dans plein de tickets sur StakOverflow (ex: Question #67418153).
En bref : il faut modifier le fichier build.gradle.

Malheureusement, vous n'avez peut-être pas accès à ce fichier, si c'est une Plateforme d'Intégration Continue qui se charge du build.
Dans ce cas, ce fichier n'est probablement pas versionné, mais générée automatiquement dans un répertoire nommé platforms/.

Entre chaque build, veillez à bien supprimer ce répertoire platforms/. Ainsi, le fichier build.gradle sera régénéré en utilisant des dépôts corrects.

Astuce Liste des caractères unicode d'espaces spéciaux

Selon la langue, différents caractères d'espace peuvent être utilisés.

En français par exemple (même si les claviers ne nous en facilitent pas l'écriture), l'espace entre des guillemets et le mot qu'ils encadrent n'est normalement pas d'une taille normale. Même chose avant :.
Les éditeurs de traitement de texte (comme LibreOffice ou Word) connaissent mieux que nous cette norme d'écriture et font le remplacement automatiquement.

Quand on récupère une chaîne qui contient de tels espaces, cela peut poser des problèmes d'affichage (ex: génération d'un PDF).

Voici la liste de ces caractères spéciaux, pour PHP :

$spaceUnicodeChars = [
            '\x{0020}',
            '\x{00A0}',
            '\x{180E}',
            '\x{2000}',
            '\x{2001}',
            '\x{2002}',
            '\x{2003}',
            '\x{2004}',
            '\x{2005}',
            '\x{2006}',
            '\x{2007}',
            '\x{2008}',
            '\x{2009}',
            '\x{200A}',
            '\x{200B}',
            '\x{202F}',
            '\x{205F}',
            '\x{3000}',
            '\x{FEFF}',
        ];

Pour javascript :

const SPACE_UNICODE_CHARS = [
    '\u{0020}',
    '\u{00A0}',
    '\u{180E}',
    '\u{2000}',
    '\u{2001}',
    '\u{2002}',
    '\u{2003}',
    '\u{2004}',
    '\u{2005}',
    '\u{2006}',
    '\u{2007}',
    '\u{2008}',
    '\u{2009}',
    '\u{200A}',
    '\u{200B}',
    '\u{202F}',
    '\u{205F}',
    '\u{3000}',
    '\u{FEFF}',
  ];

Astuce Utiliser les promesses

Introduction

Lorsqu'on fait un appel à un webservice ou qu'on exécute une grosse requête en base de données, cela prend un certain temps. On sait ce qu'on est sensé récupérer mais pas quand, ce qui semble plutôt gênant pour lancer la suite du traitement (ex: afficher les résultats).

Ce qu'on appelle une promesse, c'est ce qu'on espère recevoir comme résultat (un tableau, un flux, un objet, ... ce qu'on veut). On code la suite du traitement à réaliser comme si on avait ce résultat, sauf qu'on part de l'objet Promise à la place.

Exemples

Si on n'utilise pas les promesses et qu'on utilise une fonction asynchrone, cela peut ressembler à ça :

function getNumberFromWebservice(successCllback, errorCallback) {
    try {
        // On récupère les données
        let number = xxx();
        successCllback(number);
    } catch {
        errorCallback('Impossible de récupérer le nombre.');
    }
}

getResultsFromWebservice(function (number) {
    console.log('Affichage du résultat :');
    console.log(number);
}, function (error) {
    console.error(error);
});

Cette méthode fonctionne bien, mais à quoi cela ressemblerait-il si on appelait une seconde fonction asynchrone au retour de la première ?

function getNumberFromWebservice1(successCllback, errorCallback) {
    try {
        // On récupère les données
        let number = xxx();
        successCllback(number);
    } catch {
        errorCallback('Impossible de récupérer le nombre.');
    }
}

function getNumberFromWebservice2(number1, successCllback, errorCallback) {
    try {
        // On récupère les données
        let number2 = yyy();
        successCllback(number1 + number2);
    } catch {
        errorCallback('Impossible de récupérer le nombre.');
    }
}

function displayResult(sum) {
    console.log('Affichage de la somme :');
    console.log(sum);
}

function displayError(error) {
    console.error(error);
}

// /!\ Début de callback hell /!\
getResultsFromWebservice1(function (number1) {
    getResultsFromWebservice2(number1, function (sum) {
        displayResult(sum);
    }, function (error) {
        displayError(error);
    });
}, function (error) {
    displayError(error);
});

Ca devient vraiment peu lisible dès qu'on multiplie les fonctions asynchrones à chaîner. On a des callback de callback de callback (cf. Callback hell).

Les promesses permettent une syntaxe plus lisible :

// Pas de callback hell, on évite l'écriture en imbrication
getResultsFromWebservice1()
    .then(function(number1) { 
        return getResultsFromWebservice2(number1);
    }),
    .then(function(sum) {
        console.log('Affichage de la somme :');
        console.log(sum);
    }),
    .catch(function (error) {
        console.error(error);
    });

// Ou même plus court avec la syntaxe allégée
getResultsFromWebservice1()
    .then((number1) => getResultsFromWebservice2(number1)),
    .then((sum) => displayResult(sum)),
    .catch((error) => displayError(error));

Le principe est le même sauf qu'on n'a plus besoin de passer les callback en paramètres à chaque fonction. Par contre pour que ça marche, il faut modifier un peu chaque fonction pour qu'elle ne retourne non plus un nombre mais un objet Promise :

function getNumberFromWebservice1() {
    return new Promise((resolve, reject) => {
        // On récupère les données
        let number = xxx();
        resolve(number);
    });
}

function getNumberFromWebservice2(number1) {
    return new Promise((resolve, reject) => {
        // On récupère les données
        let number2 = yyy();
        resolve(number1 + number2);
    });
}

Remarque :

Les try/catch ont été retirés dans les fonctions. Cela veut dire qu'en a cas d'erreur levée par l'appel aux webservices (xxx() et yyy()), ce sera le message de l'erreur qui sera affiché, et non plus le message 'Impossible de récupérer le nombre.'.

Aller plus loin

Les promesses existent depuis un moment en javascript. Il y a pas mal de cas d'utilisation :

  • exécuter trois tâches asynchrones une à une
  • exécuter trois tâches asynchrones une à une et en transmettant à chaque fois le résultat à la suivante
  • exécuter plusieurs tâches asynchrones en parallèle, puis une dernière quand elles sont toutes terminées
  • exécuter 2 séries de 3 tâches asynchrones en parallèle
  • ...

La plupart des framework proposent une couche supplémentaire pour gérer ces cas d'utilisation. Dans le monde de NodeJS par exemple, on utilise très souvent la lib async.

Documentation Mozilla Développeur :

Marque-page Introduction à NodeJS

Exercices

Pour faire ses premiers pas avec NodeJS, le site nodeschool.io propose des ateliers de développement.

Concrètement, il s'agit de petites applications NodeJS qui affichent un énoncé, et sont capables d'éxécuter votre programme pour vérifier qu'il correspond aux consignes. L'un des ateliers utiles pour débuter s'appelle learnyounode.

Accueil learnyounode

Pour l'utiliser il vous faut NodeJS :

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

Ensuite ça se passe avec npm (inclus vec NodeJS) :

npm install -g learnyounode
learnyounode

Express

Le framework Express semble très largement répandu dans l'univers de NodeJS, dès lors qu'on souhaite créer une application web (API REST, site front, ...).

Mozilla Developpeur propose un bon tutoriel pour démarrer avec Express.
Il s'agît d'un cas concret de création d'appplication web, pour gérer une bibliothèque (de livres).
Une partie front interroge un back REST full qui stocke les données dans une base MongoDB.

(Il y a également un atelier sur le sujet proposé sur nodeschool.io)

Mongoose

Pour utiliser une base MongoDB avec NodeJS, il existe l'API Mongoose.
(Elle est utilisée dans le tutoriel précédent.)

Astuce Bonnes pratiques jQuery

Isoler son code

Pour éviter que ses variables et fonctions ne soient disponibles partout et ne rentrent en conflit entre-elles, il est important d'isoler son code.

Pour cela, plusieurs solutions sont possibles, en fonction des cas.

Code à usage unique

Cela consiste à englober son code pour que les variables et fonctions deviennent locales.

(function($) {

    // Code à exécuter
    // [...]
})(jQuery);

Code à usage régulier

Cela consiste à placer ses variables et fonctions dans un namespace autre que celui par défaut (= lorsqu'on n'en précise pas).

$.myNamespace = {
    multiply: function(x,y) {
        return (x * y);
    }
};

// Appel
var calc = $.myNamespace.multiply(1, 1);

Code affectant des éléments du DOM 1

On utilise le namespace de jQuery : $ ou jQuery.

$.fn.myFunction = function(params) {

    this.css('color', 'red');
    return this;
};

// Appel
$('.element').myFonction();

Explication :

On crée un plugin jQuery, que l'on appelle sur un ensemble d'éléments du DOM (ex: $('.element')). Tous ces éléments sont impactés une fois, sans mémorisation de leur état (= stateless).

Code affectant des éléments du DOM 2

$.widget('ui.trsb.truc.myWidget', [$.ui.widgetParent,]{
    "options": {},
    myWidgetFunction(){...},
    _create: function(){...},
    _destroy: function(){...},
    _setOptions: function(key, value){...},
});

// Appel
$('.element').myWidget();
$('.element').myWidgetFunction();

Explications :

On crée un widget jQuery (nécessite jQueryUI), que l'on appelle sur un ensemble d'éléments du DOM (ex: $('.element')). Tous ces éléments sont impactés avec mémorisation de leur état (= statefull).

Les widgets sont utiles lorsque l'on veut proposer plusieurs méthodes d'action (ex: un accordéon avec une fonction pour dérouler, et une autre pour replier).

Normes de codage et optimisations

Sélecteurs

// Pour des raisons de compatibilité, préférez
$('#container').find('.children').hide();

// à 
$('#container .children').hide();

// ou à 
$('.children', '#container').hide();

Éviter le tout jQuery

$('#link').on('click', function() {

    // Préférez
    this.id

    // à
    $this.attr(id');
});

Chargement du DOM

// Préférez 
$(function() {

});

// à 
$(document).ready(function() {

});

Gestion des évènements

// Préférez 
$('#element').on('click', function() {

});

// à 
$('#element').click(function() {

});

No conflict

// Préférez 
(function($) {
 // [...] Code sans conflit
})(jQuery);

// à 
jQuery.noConflict();
// [...] Code sans conflit

Appels AJAX

// Préférez 
$.ajax()

// à 
$.getJSON();

Astuce Utiliser les media queries en JS

Dans une feuille CSS, si vous voulez appliquer un style particulier à élément, pour un petit écran, vous faites quelque chose comme ça :

@media only screen and (max-width: 36.9375em) {
    .col {
        float: none;
        width: 100%;
    }
}

Il est possible d'utiliser une syntaxe similaire en Javascript :

if (window.matchMedia != undefined
    && window.matchMedia('(max-width: 53.6875em)').matches) {
    alert('Mon écran est tout petit !').
}

Explication :

On vérifie que le navigateur comprend cette syntaxe et si oui on ajoute une condition sur la taille de la fenêtre.

Astuce Savoir si un case à cocher est cochée

Si dans un formulaire vous avez une checkbox et que vous voulez savoir si elle est cochée, vous pouvez utiliser la fonction is() de jQuery :

jQuery('#ma_checkbox_id').is(':checked')

Par exemple, pour loguer un message lorsqu'on coche/décoche une checkbox :

jQuery('#ma_checkbox_id').on('change', function() {

    // Si la checkbox est cochée
    if (jQuery(this).is(':checked')) {

        console.log('Je viens d\'être cochée !');

    } else {

        console.log('Je viens d\'être décochée !');
    }
});

Astuce Placer une boîte de dialogue au niveau d'un bouton

Par défaut, les boîtes de dialogue de jQuery UI sont centrées verticalement et horizontalement. Cependant, le centrage vertical n'est pas très intelligent et agit par rapport à la hauteur de l'écran, peu importe où on est dans la page.

Imaginons par exemple que votre écran fasse 1200px de hauteur et votre page 2000px. Placez un bouton qui ouvre la pop-up tout en bas de la page, scrollez jusqu'à lui et cliquez dessus. La pop-in s'ouvrira à 600px (environ) du haut de la page, alors que vous serez tout en bas.

Voici une astuce pour la placer à hauteur du bouton :

jQuery('#my-button').on('click', function(event) {

    event.preventDefault();

    jQuery('#my-dialog-confirm').dialog({
        "resizable": false,
        "draggable": false,
        "modal": true,
        "buttons" : {
            "Oui" : function() { 
                jQuery(this).dialog("close");

                // Traitement à effectuer
                // [...]
            },
            "Non" : function() {                
                jQuery(this).dialog("close");
            }
        }
    });

    // Positionnement de la pop-in
    var topOffset = jQuery(this).offset().top - (jQuery('#my-dialog-confirm').height() / 2);
    jQuery('#my-dialog-confirm').dialog( 'option', 'position', ['center', topOffset] );
});

Explications :

  • Au clic sur le bouton, l'élément avec l'ID `my-dialog-confirm``` s'ouvre en fenêtre de dialogue de confirmation, avec les boutons Oui et Non.
  • Après l'ouverture de la pop-in, on la positionne au centre de l'écran en largeur, et la centre en hauteur au niveau du bouton.

Astuce Rediriger une page depuis une iframe

Si vous êtes dans une iframe et que vous souhaitez rediriger la page qui la contient, vous pouvez utiliser ces quelques lignes :

var url = "https://targeturl.com"

// Si on est dans une iframe
if (frameElement) {                
    window.top.location.href = url;
}

Remarque : Ce code javascript doit se trouver dans l'iframe.

Erreur La méthode parseInt() de Javascript et les nombres commençant par 0

Si vous utilisez la méthode parseInt() pour parser des chaînes de caractères représentant des nombres commençant par 0, vous pouvez rencontrer ce problème :

var test;
test = parseInt("01"); // test = 1
test = parseInt("02"); // test = 2
test = parseInt("03"); // test = 3
test = parseInt("04"); // test = 4
test = parseInt("05"); // test = 5
test = parseInt("06"); // test = 6
test = parseInt("07"); // test = 7
test = parseInt("08"); // test = 0
test = parseInt("09"); // test = 0

Sous Internet Explorer 8 et inférieur, la méthode parseInt() considère les nombres commençant par 0 comme étant en base 8.

Il faut donc utiliser le second paramètre de la méthode, pour préciser qu'on veut travailler en base 10 :

var test;
test = parseInt("01", 10); // test = 1
test = parseInt("02", 10); // test = 2
test = parseInt("03", 10); // test = 3
test = parseInt("04", 10); // test = 4
test = parseInt("05", 10); // test = 5
test = parseInt("06", 10); // test = 6
test = parseInt("07", 10); // test = 7
test = parseInt("08", 10); // test = 8
test = parseInt("09", 10); // test = 9

Astuce Scroller automatiquement jusqu'à un élément

Voici une petite fonction pour scroller automatiquement vers le haut jusqu'à un élément :

/**
 * Scrolle automatiquement vers le haut jusqu'à l'élément dont le sélecteur est en argument.
 *
 * @param selector string Sélecteur de l'élément vers lequel scroller
 * @param nbMs Nombre de millisecondes pour effectuer l'animation de scroll (facultatif, 10 par défaut)
 */
function scrollToElement(selector, nbMs) {

    nbMs = nbMs || 10;
    var documentBody = (($.browser.chrome)||($.browser.safari)) ? document.body : document.documentElement;
    jQuery(documentBody).animate({scrollTop: jQuery(selector).offset().top}, nbMs, 'linear');
}

Remarques :

  • JQuery doit être inclus dans la page.
  • Depuis jQuery 1.9, la propriété browser a été retirée et transférée dans le plugin jQuery Migrate. Il faut donc ajouter ce plugin pour pouvoir l'utiliser.

Marque-page Timepicker

JQuery UI fournit un datepicker pour agrémenter les champs date d'un petit calendrier. Malheureusement il ne propose pas l'équivalent pour un champ heure.

Premier Datepicker

C'est ce que propose ce site : http://trentrichardson.com/examples/timepicker/.

Premier timepicker

Ce timepicker étends jQuery UI et permet de le combiner ou non avec le datepicker déjà existant.

Remarque :

Le timepicker a été traduit dans de nombreuses langues. Les traductions sont disponibles sur ce repo git.

Second Datepicker

Si vous utilisez le framework Bootstrap, ce site propose un autre timepicker un peu plus agréable à utiliser : http://jdewit.github.io/bootstrap-timepicker/.

Second timepicker

Astuce Redimensionner automatiquement la hauteur d'une iframe

Si vous utilisez une iframe dans une page, vous pouvez fixer sa largeur et sa hauteur. Si vous ne le faite pas, ses dimensions seront limitées par le navigateur et tout le contenu de l'iframe ne sera pas forcément visible sans scroller.

Pour agrandir l'iframe en fonction de son contenu, vous pouvez utiliser cette fonction :

/**
 * Redimensionne automatiquement la hauteur de l'iframe contenant la page courante.
 * (La page courante est celle dans laquelle est appelée cette fonction.)
 *
 * @param marginHeight Marge verticale en pixel à appliquer (facultatif, 25 par défaut)
 */
function autoResizeIFrame(marginHeight) {

    marginHeight = marginHeight || 25;

    var id;

    // Si la page courante est dans une iframe
    if (frameElement) { 
        try { 
            id = frameElement.attributes['id'].value; 
        } catch (e) { 
            if (window.console && console.log) {
                console.log(e.message);
            }
        }
    }

    // Si la page contenant l'iframe a un ID
    if (id) {    
        if (document.getElementById) {        
            // Firefox
            if (navigator.appName == 'Netscape') {            
                parent.document.getElementById(id).style.height = this.document.body.offsetHeight + marginHeight + 'px';
            // IE
            } else {                        
                marginHeight += 5;
                parent.document.getElementById(id).style.height = this.document.body.scrollHeight + marginHeight + 'px';
            }            
        // Opera
        } else if (!window.opera && !document.mimeType && document.all && document.getElementById) {        
            marginHeight -= 7;
            parent.document.getElementById(id).style.height = this.document.body.offsetHeight + marginHeight + 'px';
        }

        // Retour au haut de la page
        window.parent.scrollTo(0, 0);
    }
}

Pour l'utiliser, il suffit d'appeler la fonction depuis l'iframe, à la fin du chargement de la page (ou après avoir modifier le contenu de l'iframe via Javascript).

Remarque :

Cette fonction n'est utilisable que si la page qui inclue l'iframe et la page à l'intérieur ont le même domaine. Cette restriction est une sécurité propre à Javascript pour éviter à une page de modifier le contenu d'une autre.

Astuce Savoir quel bouton radio est sélectionné

Si dans un formulaire vous avez deux boutons radio "Oui"/"Non et que vous avez besoin de savoir lequel est sélectionné, vous pouvez utiliser la fonction suivante :

/**
 * Retourne si l'élément radio est à true.
 * 
 * @param elementName Nom de l'élément (attribut "name")
 */
function isChecked(elementName) {

    var element = jQuery('input[name=' + elementName + ']:checked');

    return element != null && element.val() == 1;
}

Remarques :

  • Cette fonction suppose que le bouton radio "Oui" a pour valeur 1 et l'autre 0.
  • Cette méthode peut être utilisée pour une liste de radio plus importante. Il suffit de modifier le test element.val() == 1 par une autre valeur que 1.