vendredi 28 décembre 2012

Comment créer un fichier d'entrée personnalisé avec jQuery, CSS3 et PHP

Nous savons tous que les entrées de fichiers sont très limitées en termes de personnalisation et bien qu'il existe de nombreux plugins alambiquées avec des dizaines de solutions de repli qui vous permettent de les personnaliser, c'est toujours un défi parfois de le faire fonctionner. Ce tutoriel vous guidera à travers le processus de création d'un plugin jQuery pour remplacer ce vilain entrée à la recherche avec le soutien de plusieurs fichiers et un repli simple pour les vieux navigateurs, ahem, IE9-8.
Comment créer un fichier d'entrée personnalisé avec jQuery, CSS3 et PHP

Comment créer un fichier d'entrée personnalisé avec jQuery, CSS3 et PHP

Mise en place du projet

D'abord nous allons créer un dossier customFile et 3 photos, jquery.customFile.js , jquery.customFile.css et customFile.html . Pour commencer rapidement, saisissez cette template HTML et copiez / collez-le dans votre projet.
Maintenant que nous avons tout ce dont nous avons besoin nous allons ouvrir notre fichier HTML et ajouter un conteneur et un fichier d'entrée avec son étiquette:
<div class="customfile-container">
  <label>File: </label>
  <input type="file" id="file" name="myfiles[]" multiple />
</div>
Assurez-vous également de lui donner un id et un nom de tableau, comme myfiles[] de sorte que le serveur peut récupérer tous les fichiers avec le repli IE qui sera traité plus tard.
Suivant ouverts jquery.customFile.js et configurer une base réutilisable jQuery plugin:
;(function( $ ) {

  $.fn.customFile = function() {

    return this.each(function() {

      var $file = $(this).addClass('customfile'); // the original file input

      // code here

    });

  };

}( jQuery ));
Enfin, appelez le plugin dans votre balisage:
<script>$('input[type=file]').customFile()</script>

Comment ça marche?

Pour construire le remplacement sur mesure nous avons besoin d'une structure de balisage simple:
Concept
En cliquant sur le bouton "ouvrir" déclenchera un événement "click" sur le fichier d'entrée d'origine. Après avoir choisi un fichier d'entrée d'origine déclenche un événement «change», où nous allons définir la valeur de l'entrée en accédant à l'ensemble du fichier si le fichier est pris en charge l'API, ou en accédant à la valeur d'origine autrement.

Construire le Plugin

Premièrement, nous devons tester le navigateur de multiple soutien. Le plus simple est de créer une entrée et vérifier si elle a un multiple propriété disponible sinon le navigateur ne prend pas en charge les fichiers multiples. Nous devons également vérifier si le navigateur est IE pour quelques corrections par la suite. Ce code peut être déplacé à l'extérieur du plugin, car elle ne dépend pas de l'élément lui-même.
// Browser supports HTML5 multiple file?
var multipleSupport = typeof $('<input/>')[0].multiple !== 'undefined',
    isIE = /msie/i.test( navigator.userAgent ); // simple but not super secure...
Maintenant, nous allons créer les éléments nécessaires pour le remplacement. IE des mesures de sécurité strictes qui empêchent le nom de fichier en cours de récupération si l'entrée est déclenchée à l'extérieur donc nous allons utiliser une label au lieu d'un button . En déclenchant l'événement sur l'étiquette on peut contourner ce problème.
var $wrap = $('<div class="customfile-wrap">'),
    $input = $('<input type="text" class="customfile-filename" />'),
    $button = $('<button type="button" class="customfile-upload">Open</button>');
    $label = $('<label class="customfile-upload" for="'+ $file[0].id +'">Open</label>');
Le type="button" attribut est nécessaire pour la cohérence, pour éviter certains navigateurs de soumettre le formulaire.
Ensuite, nous allons «se débarrasser» de l'entrée d'origine. Au lieu de le cacher, nous allons retirer de la fenêtre en la déplaçant vers la gauche, de cette façon on peut encore l'utiliser même si elle n'est pas visible, ce qui est utile pour déclencher des événements qui peuvent être problématiques si l'entrée est littéralement caché.
$file.css({
  position: 'absolute',
  left: '-9999px'
});
Enfin nous allons ajouter nos nouveaux éléments au DOM:
$wrap.insertAfter( $file ).append( $file, $input, ( isIE ? $label : $button ) );
À ce stade, vous devriez avoir quelque chose qui ressemble à ça dans un navigateur décent, nous nous occupons de IE plus tard.
Étape 1

Fixation des événements

La première chose que nous devons faire est d'empêcher l'entrée d'origine de gagner mise au point, ainsi que le bouton nouvellement créé. Seule la saisie de texte doit être en mesure de recevoir le focus.
$file.attr('tabIndex', -1);
$button.attr('tabIndex', -1);
Nous allons déclencher le click événement sur ​​le bouton pour ouvrir la boîte de dialogue. Dans IE, car il n'ya pas de vrai bouton, l'étiquette doit déjà déclencher le dialogue sans travail supplémentaire.
$button.click(function () {
  $file.focus().click(); // Open dialog
});
L' focus événement doit être déclenché sur certains navigateurs ainsi l'événement de clic fonctionne correctement. Si vous essayez de cliquer "ouvert" dans votre navigateur, à ce stade, il convient d'ouvrir la boîte de dialogue de fichier.
Maintenant, nous pouvons utiliser le change événement qui est déclenché après avoir choisi un fichier pour remplir la valeur de la saisie de texte avec le fichier choisi (s).
$file.change(function() {

    var files = [], fileArr, filename;

    // If multiple is supported then extract
    // all filenames from the file array
    if ( multipleSupport ) {
      fileArr = $file[0].files;
      for ( var i = 0, len = fileArr.length; i < len; i++ ) {
        files.push( fileArr[i].name );
      }
      filename = files.join(', ');

    // If not supported then take the value
    // and remove the path to show only the filename
    } else {
      filename = $file.val().split('\\').pop();
    }

    $input.val( filename ) // Set the value
      .attr('title', filename) // Show filename in title tootlip
      .focus(); // Regain focus

});

Améliorer la convivialité

Tout fonctionne bien jusqu'à présent, mais il ya certaines choses que nous pouvons faire pour améliorer la convivialité et le comportement global de notre nouveau remplacement.
  • Déclencher le blur événement sur ​​l'entrée d'origine lorsque le remplacement perd le focus.
  • Ouvrez la boîte de dialogue lorsque l'utilisateur appuie sur "Entrée" sur la saisie de texte, sauf IE (en raison des limitations de sécurité ça ne marchera pas).
  • Supprimer les fichiers avec "backspace" ou "delete", sinon, l'utilisateur est contraint d'ouvrir la boîte de dialogue et appuyez sur "annuler" pour effacer l'entrée.
En d'autres termes:
$input.on({
  blur: function() { $file.trigger('blur'); },
  keydown: function( e ) {
    if ( e.which === 13 ) { // Enter
      if ( !isIE ) { $file.trigger('click'); }
    } else if ( e.which === 8 || e.which === 46 ) { // Backspace & Del
      // On some browsers the value is read-only
      // with this trick we remove the old input and add
      // a clean clone with all the original events attached
      $file.replaceWith( $file = $file.clone( true ) );
      $file.trigger('change');
      $input.val('');
    } else if ( e.which === 9 ){ // TAB
      return;
    } else { // All other keys
      return false;
    }
  }
});

Repli pour les anciens navigateurs

Le plus simple repli pour permettre aux fichiers multiples est de créer des entrées multiples. Quand un fichier est choisi, nous créons une nouvelle entrée, lorsque l'entrée est effacée, nous l'enlever.
Le code suivant va après le plugin car il est destiné à être appliqué à toutes les entrées de fichiers personnalisés. Ici, nous avons besoin d'utiliser on de déléguer le cas pour les entrées futures qui n'existent pas encore.
if ( !multipleSupport ) {
  $( document ).on('change', 'input.customfile', function() {

    var $this = $(this),
        // Create a unique ID so we
        // can attach the label to the input
        uniqId = 'customfile_'+ (new Date()).getTime();
        $wrap = $this.parent(),

        // Filter empty input
        $empty = $wrap.siblings().find('.customfile-filename')
          .filter(function(){ return !this.value }),

        $file = $('<input type="file" id="'+ uniqId +'" name="'+ $this.attr('name') +'"/>');

    // 1ms timeout so it runs after all other events
    // that modify the value have triggered
    setTimeout(function() {
      // Add a new input
      if ( $this.val() ) {
        // Check for empty field to prevent
        // creating new inputs when changing files
        if ( !$empty.length ) {
          $wrap.after( $file );
          $file.customFile();
        }
      // Remove and reorganize inputs
      } else {
        $empty.parent().remove();
        // Move the input so it's always last on the list
        $wrap.appendTo( $wrap.parent() );
        $wrap.find('input').focus();
      }
    }, 1);

  });
}

Personnalisation de l'apparence

Tout doit être travaillent à cet endroit afin épices Regardons les choses en place avec certains styles:
/* It's easier to calculate widths 
 * with border-box layout */
.customfile-container * {
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  font: normal 14px Arial, sans-serif; /* Global font to use ems with precision */
}

.customfile-container {
  width: 300px;
  background: #FFF2B8;
  padding: 1em;
}

.customfile-container label:first-child {
  width: 100px;
  display: block;
  margin-bottom: .5em;
  font: bold 18px Arial, sans-serif;
  color: #333;
}

.customfile-wrap {
  position: relative;
  padding: 0;
  margin-bottom: .5em;
}

.customfile-filename,
.customfile-upload { 
  margin: 0;
  padding: 0;
}

.customfile-filename {
  width: 230px;
  padding: .4em .5em;
  border: 1px solid #A8A49D;
  border-radius: 2px 0 0 2px;
  box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
}
.customfile-filename:focus { 
  outline: none;
}

.customfile-upload {
  display: inline-block;
  width: 70px;
  padding: .4em 1em;
  border: 1px solid #A8A49D;
  background: #ddd;
  border-radius: 0 2px 2px 0;
  margin-left: -1px; /* align with input */
  cursor: pointer;
  background: #fcfff4;
  background: -moz-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
  background: -webkit-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
  background: -o-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
  background: -ms-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
  background: linear-gradient(to bottom, #fcfff4 0%, #e9e9ce 100%);
}

.customfile-upload:hover {
  background: #fafafa;
  box-shadow: 0 0 2px rgba(0,0,0,.2);
}
.customfile-upload::-moz-focus-inner { /* Fix firefox padding */
  padding: 0; border: 0;
}
Allez-y et personnaliser le CSS pour créer votre propre look.

Récupération des fichiers sur le serveur

Tout d'abord, enveloppez votre entrée dans un formulaire et ajoutez un bouton d'envoi:
<form action="test.php" method="post" enctype="multipart/form-data">
  <div class="customfile-container">
    <label>File: </label>
    <input type="file" id="file" name="myfiles[]" multiple />
  </div>
<button type="submit">Submit</button>
</form>
Ensuite, nous pouvons obtenir tous les noms de fichiers et de les imprimer dans test.php :
<?php
$files = $_POST['myfiles']; // Array containing all files
echo implode( $files, '<br/>' );
Comme nous utilisons un nom de tableau myfiles[] , le serveur va récupérer tous les fichiers, même lorsque le repli est utilisé. Vous pouvez en lire plus à ce sujet dans le manuel PHP sur Téléchargement de plusieurs fichiers .

Conclusion

Sans beaucoup d'effort entrées de fichiers sont assez faciles à personnaliser. Le repli n'est évidemment pas idéal, mais ça fonctionne et c'est simple à entretenir sans introduire des centaines de lignes de code, ou d'autres technologies comme Flash, Silverlight, etc ..
Le plugin a été testé sur IE9-8 et tous les navigateurs modernes. Saisissez le code complet ou de jouer avec la démo . Si vous avez des suggestions s'il vous plaît laissez un commentaire ci-dessous.

Aucun commentaire:

Enregistrer un commentaire

Nous vous invitons ici à donner votre point de vue, vos informations, vos arguments. Nous refusons les messages haineux, diffamatoires, racistes ou xénophobes, les menaces, incitations à la violence ou autres injures. Merci de garder un ton respectueux et de penser que de nombreuses personnes vous lisent.
La rédaction

Messages les plus consultés