Utilisez des images comme des cases à cocher

165

Je voudrais avoir une alternative à une case à cocher standard - en gros, j'aimerais utiliser des images et lorsque l'utilisateur clique sur l'image, la fondre et superposer une case à cocher.

En gros, je veux faire quelque chose comme Recaptcha 2 quand il vous amène à cliquer sur des images qui répondent à certains critères. Vous pouvez voir une démo de Recaptcha ici, mais cela peut parfois vous amener à résoudre des questions de texte, par opposition à la sélection d'images. Voici donc une capture d'écran:

Capture d'écran de Google Recaptcha

Lorsque vous cliquez sur l'une des images (dans ce cas, contenant une image de steak), la taille de l'image sur laquelle vous cliquez diminue et la coche bleue apparaît, indiquant que vous l'avez cochée.

Disons que je veux reproduire cet exemple exact.

Je me rends compte que je peux avoir 9 cases à cocher cachées et attacher du jQuery de sorte que lorsque je clique sur l'image, il sélectionne / désélectionne la case à cocher cachée. Mais qu'en est-il du rétrécissement de l'image / superposition de la tique?

bgs264
la source
2
vous pouvez avoir deux images identiques: une avec tick et l'autre sans tick. Et changer les images en un clic
Alex
23
@Alex Ce serait une solution particulièrement peu flexible.
Siguza
2
Avez-vous essayé d'ajouter / supprimer une classe CSS sur clic qui remplace la taille de l'image et fait de la :beforemagie pour l'image de tique?
Hexaholic
3
@Alex "Unflexible" dans le sens où il faudrait beaucoup plus de travail pour changer quoi que ce soit, comme ajouter une nouvelle image à la collection ou changer l'icône "cochée", alors que ce ne serait pas le cas si l'effet est créé par programme à Durée.
Siguza
2
@Christian Remplacer les cases à cocher par un élément personnalisé est une décision de conception populaire (même s'il ne s'agit que d'une case à cocher stylée), et trouver des moyens simples / bons de le faire est certainement intéressant pour les développeurs qui ont dû y faire face. Et cette question (superposer le remplacement de la case à cocher sur autre chose) en fait définitivement une question intéressante. En particulier, notez comment la réponse acceptée n'utilise pas du tout de JS, malgré l'apparente complexité de la tâche
Izkata

Réponses:

309

Solution HTML / CSS sémantique pure

Ceci est facile à mettre en œuvre par vous-même, aucune solution prédéfinie n'est nécessaire. De plus, cela vous apprendra beaucoup car vous ne semblez pas trop facile avec CSS.

Voici ce que vous devez faire:

Vos cases à cocher doivent avoir des idattributs distincts . Cela vous permet d'y connecter un <label>, en utilisant l' forattribut de l' étiquette.

Exemple:

<input type="checkbox" id="myCheckbox1" />
<label for="myCheckbox1"><img src="http://someurl" /></label>

Le fait d'attacher l'étiquette à la case à cocher déclenchera un comportement du navigateur: chaque fois que quelqu'un cliquera sur l'étiquette (ou l'image à l'intérieur), la case à cocher sera activée.

Ensuite, vous masquez la case à cocher en y appliquant par exemple display: none;.

Il ne vous reste plus qu'à définir le style que vous souhaitez pour votre label::beforepseudo élément (qui sera utilisé comme élément de remplacement visuel de la case à cocher):

label::before {
    background-image: url(../path/to/unchecked.png);
}

Dans une dernière étape délicate, vous utilisez le :checkedpseudo-sélecteur CSS pour changer l'image lorsque la case est cochée:

:checked + label::before {
    background-image: url(../path/to/checked.png);
}

Le +( sélecteur frère adjacent ) garantit que vous ne modifiez que les libellés qui suivent directement la case à cocher masquée dans le balisage.

Vous pouvez optimiser cela en plaçant les deux images dans un spritemap et en n'appliquant qu'une modification au background-positionlieu de permuter l'image.

Bien sûr, vous devez positionner correctement l'étiquette et appliquer display: block;et régler correctement widthet height.

Éditer:

L'exemple et l'extrait de code codepen, que j'ai créés après ces instructions, utilisent la même technique, mais au lieu d'utiliser des images pour les cases à cocher, les remplacements de cases à cocher se font uniquement avec CSS , créant un ::beforesur l'étiquette qui, une fois cochée, a content: "✓";. Ajoutez des bordures arrondies et des transitions douces et le résultat est vraiment sympathique!

Voici un codepen fonctionnel qui présente la technique et ne nécessite pas d'images pour la case à cocher:

http://codepen.io/anon/pen/wadwpx

Voici le même code dans un extrait de code:

ul {
  list-style-type: none;
}

li {
  display: inline-block;
}

input[type="checkbox"][id^="cb"] {
  display: none;
}

label {
  border: 1px solid #fff;
  padding: 10px;
  display: block;
  position: relative;
  margin: 10px;
  cursor: pointer;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

label::before {
  background-color: white;
  color: white;
  content: " ";
  display: block;
  border-radius: 50%;
  border: 1px solid grey;
  position: absolute;
  top: -5px;
  left: -5px;
  width: 25px;
  height: 25px;
  text-align: center;
  line-height: 28px;
  transition-duration: 0.4s;
  transform: scale(0);
}

label img {
  height: 100px;
  width: 100px;
  transition-duration: 0.2s;
  transform-origin: 50% 50%;
}

:checked+label {
  border-color: #ddd;
}

:checked+label::before {
  content: "✓";
  background-color: grey;
  transform: scale(1);
}

:checked+label img {
  transform: scale(0.9);
  box-shadow: 0 0 5px #333;
  z-index: -1;
}
<ul>
  <li><input type="checkbox" id="cb1" />
    <label for="cb1"><img src="https://picsum.photos/seed/1/100" /></label>
  </li>
  <li><input type="checkbox" id="cb2" />
    <label for="cb2"><img src="https://picsum.photos/seed/2/100" /></label>
  </li>
  <li><input type="checkbox" id="cb3" />
    <label for="cb3"><img src="https://picsum.photos/seed/3/100" /></label>
  </li>
  <li><input type="checkbox" id="cb4" />
    <label for="cb4"><img src="https://picsum.photos/seed/4/100" /></label>
  </li>
</ul>

connexo
la source
32
Content que tu aimes ça! Commencez à bidouiller davantage avec CSS, c'est très amusant et CSS peut faire beaucoup plus que ce à quoi vous vous attendiez probablement (sauf le centrage vertical, c'est tout simplement trop difficile;)).
connexo
1
Ouais, et comprendre son fonctionnement vous fera réfléchir à l'avenir, chaque fois que vous voudrez une action DOM sur clic. Et vous pensiez pouvoir réagir uniquement à :hover:)
connexo
Ce sera ma prochaine chose à regarder sur Pluralsight - quelques trucs CSS :)
bgs264
6
C'est très gentil! J'ai remarqué un petit problème lorsque vous cliquez deux fois sur la même étiquette, cela sélectionne l'image. Cela corrige le problème: codepen.io/anon/pen/jPmNjg
Ayesh K
1
Tick ​​aurait mieux fait de SVG. Le contenu CSS vous met à la merci de certaines polices système terribles.
Adria
31

Solution CSS pure

Il y a trois appareils invoqués:

  1. Le :checkedsélecteur
  2. Le ::beforepseudo-sélecteur
  3. La contentpropriété css .

label:before {
  content: url("https://cdn1.iconfinder.com/data/icons/windows8_icons_iconpharm/26/unchecked_checkbox.png");
  position: absolute;
  z-index: 100;
}
:checked+label:before {
  content: url("https://cdn1.iconfinder.com/data/icons/windows8_icons_iconpharm/26/checked_checkbox.png");
}
input[type=checkbox] {
  display: none;
}
/*pure cosmetics:*/
img {
  width: 150px;
  height: 150px;
}
label {
  margin: 10px;
}
<input type="checkbox" id="myCheckbox1" />
<label for="myCheckbox1">
  <img src="https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcR0LkgDZRDTgnDrzhnXGDFRSItAzGCBEWEnkLMdnA_zkIH5Zg6oag">
</label>
<input type="checkbox" id="myCheckbox2" />
<label for="myCheckbox2">
  <img src="https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRhJjGB3mQxjhI5lfS9SwXou06-2qT_0MjNAr0atu75trXIaR2d">
</label>
<input type="checkbox" id="myCheckbox3" />
<label for="myCheckbox3">
  <img src="https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcQuwWbUXC-lgzQHp-j1iw56PIgl_2eALrEENUP-ld72gq3s8cVo">
</label>

jcuenod
la source
Comment ajouter un comportement de bouton radio à ces cases à cocher? Je veux dire lorsque vous cliquez sur une image, l'image déjà sélectionnée est désélectionnée.
Libertad
Quiconque se pose la même question que @Libertad change simplement le type d'entrées en radio et donne-leur à tous le même nom "attribut" <input type="radio" name="myRadio" id="myRadio1" />change aussi ton css pour cacher la puce:input[type=radio]{display: none;}
Lou
8

Voir ce plugin jQuery: imgCheckbox (sur npm et bower )

Avertissement: aucun javascript n'est nécessaire pour résoudre ce problème. La tension est entre la maintenabilité et l'efficacité du code. Bien qu'il n'y ait pas besoin de plugin (ou de javascript), cela le rend certainement plus rapide à construire et souvent plus facile à changer.

Solution Barebones:

Avec du HTML très simple (pas de désordre avec les cases à cocher et les étiquettes, etc.):

<img class="checkable" src="http://lorempixel.com/100/100" />

Vous pouvez utiliser toggleClass de jQuery pour activer / désactiver une classe selectedor checkedsur l' clickévénement:

$("img.checkable").click(function () {
    $(this).toggleClass("checked");
});

Les éléments cochés sont récupérés avec

$(".checked")

Plus fraîcheur:

Vous pouvez styliser les images en fonction de cela, mais un gros problème est que sans autres éléments DOM, vous ne pouvez même pas utiliser ::beforeet ::afterajouter des éléments tels que des coches. La solution consiste à envelopper vos images avec un autre élément (et il est logique d'attacher également l'écouteur de clic à l'élément enveloppé).

$("img.checkable").wrap("<span class='fancychecks'>")

Cela laisse votre html vraiment propre et votre js incroyablement lisible. Jetez un œil à l'extrait ...

Utilisation du plugin jQuery imgCheckbox :

Inspiré par la solution ci-dessus, j'ai construit un plugin qui peut être utilisé aussi facilement que:

$("img").imgCheckbox();
  • Injecte les données des images vérifiées dans votre formulaire
  • Prend en charge les coches personnalisées
  • Prend en charge le CSS personnalisé
  • Prend en charge les éléments présélectionnés
  • Prend en charge les groupes de radio au lieu d'un simple basculement d'images
  • A des rappels d'événement
  • Valeurs par défaut sensibles
  • Léger et super facile à utiliser

Voyez-le en action (et voyez la source )

jcuenod
la source
J'aime aussi cette approche, mais elle présente un certain nombre d'inconvénients qui méritent d'être mentionnés, le plus important étant celui-ci: en supposant que l'utilisateur est censé choisir des images, les informations fournies par l'utilisateur de cette manière sont sûrement destinées à être traitées d'une manière ou d'une autre, être via un appel AJAX ou via un formulaire de soumission. Aux fins du traitement ultérieur des informations fournies par l'utilisateur, avoir des cases à cocher où un certain nombre de choix doivent être reflétés et traités est simplement la manière naturelle et sémantique de le faire.
connexo
C'est vrai, c'est sur ma liste de choses à faire pour le plugin - pour détourner la soumission de formulaire et injecter les données dans le formulaire. Mais vous avez raison, les cases à cocher sont sémantiques pour les formulaires
jcuenod
J'ai essayé d'utiliser votre plugin, mais si j'ajoute plus d'images après l'initialisation, il ne semble pas y avoir de moyen de détruire l'instance précédente afin que les nouvelles images puissent être incluses
Ian Kim
@IanKim cas d'utilisation intéressant, pouvez-vous expliquer plus sur ce que vous essayez de faire dans un problème sur github? github.com/jcuenod/imgCheckbox
jcuenod
C'est un plugin génial! veuillez publier un Webjar =)
naXa
6

Je voudrais ajouter un div supplémentaire avec position: relative;et class="checked"qui a la même largeur / hauteur que l'image a et que la position dans left: 0; top: 0;contenant l'icône. Cela commence par display: none;.

Vous pouvez maintenant écouter l' clickévénement -event:

$( '.captcha_images' ).click( function() {
    $(this + '.checked').css( 'display', 'block' );
    $(this).animate( { width: '70%', height: '70%' } );
});

De cette façon, vous pouvez obtenir l'icône et redimensionner l'image de manière plus petite.

Remarque: je voulais juste vous montrer la «logique» derrière mes pensées, cet exemple pourrait ne pas fonctionner ou contenir des bogues.

Cagatay Ulubay
la source
La technique que j'ai démontrée est si simple et robuste qu'elle est même souvent appliquée dans des situations où vous n'avez pas de case à cocher sémantiquement, comme basculer l'affichage d'autres éléments, etc. C'est le cas d'utilisation où ce n'est pas seulement la solution la plus simple et la plus flexible, mais aussi la meilleure sémantiquement.
connexo
2
Oui tu as raison. Au début, je voulais souligner votre première réponse et l'améliorer (avant de la modifier), car j'aime No-JS et les bonnes manières sémantiques, mais j'ai ensuite lu que l'OP voulait quelque chose dans jQuery alors j'ai changé ma réponse. Je me souviens où j'ai obtenu un vote défavorable, parce que le PO ne voulait pas d'une solution «meilleure» ou «la bonne manière», mais la manière exacte dont il a écrit. Peut-être qu'il y a juste quelque chose que j'ai trop lu ou que je n'ai pas compris ou que cela dépend du PO?
Cagatay Ulubay
2

J'ai remarqué que d'autres réponses ne sont pas utilisées <label>(pourquoi pas?), Ou nécessitent une correspondance foret des idattributs. Cela signifie que si vous avez des ID en conflit, votre code ne fonctionnera pas et vous devez vous rappeler de créer des ID uniques à chaque fois.

De plus, si vous masquez le inputavec display:noneou visibility:hidden, le navigateur ne se concentrera pas dessus.

Une case à cocher et son texte (ou dans ce cas, une image) peuvent être enveloppés dans une étiquette:

.fancy-checkbox-label > input[type=checkbox] {
  position: absolute;
  opacity: 0;
  cursor: inherit;
}
.fancy-checkbox-label {
  font-weight: normal;
  cursor: pointer;
}
.fancy-checkbox:before {
  font-family: FontAwesome;
  content: "\f00c";
  background: #fff;
  color: transparent;
  border: 3px solid #ddd;
  border-radius: 3px;
  z-index: 1;
}
.fancy-checkbox-label:hover > .fancy-checkbox:before,
input:focus + .fancy-checkbox:before {
  border-color: #bdbdff;
}
.fancy-checkbox-label:hover > input:not(:checked) + .fancy-checkbox:before {
  color: #eee;
}
input:checked + .fancy-checkbox:before {
  color: #fff;
  background: #bdbdff;
  border-color: #bdbdff;
}
.fancy-checkbox-img:before {
  position: absolute;
  margin: 3px;
  line-height: normal;
}
input:checked + .fancy-checkbox-img + img {
  transform: scale(0.9);
  box-shadow: 0 0 5px #bdbdff;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
<p>
  <label class="fancy-checkbox-label">
    <input type="checkbox">
    <span class="fancy-checkbox"></span>
    A normal checkbox
  </label>
</p>
<p>
  <label class="fancy-checkbox-label">
    <input type="checkbox">
    <span class="fancy-checkbox fancy-checkbox-img"></span>
    <img src="http://placehold.it/150x150">
  </label>
</p>

rybo111
la source
1

Voici un exemple rapide de sélection d'une image comme une case à cocher

Exemple mis à jour avec Knockout.js:

var imageModel = function() {
    this.chk = ko.observableArray();
};
ko.applyBindings(new imageModel());
    input[type=checkbox] {
        display:none;
      }
 
  input[type=checkbox] + label
   {
       display:inline-block;
        width:150px;
        height:150px;
        background:#FBDFDA;
        border:none;
   }
   
   input[type=checkbox]:checked + label
    {
        background:#CFCFCF;
        border:none;
        position:relative;
        width:100px;
        height:100px;
        padding: 20px;
    }

   input[type=checkbox]:checked + label:after
    {
        content: '\2713';
        position:absolute;
        top:-10px;
        right:-10px;
        border-radius: 10px;
        width: 25px;
        height: 25px;
        border-color: white;
        background-color: blue;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<input type='checkbox' name='image1' value='image1' id="image1" data-bind="checked: chk"/><label for="image1"></label><label for="image1"><img class='testbtn'/></label>

<div data-bind="html: chk"></div>

YaBCK
la source
1
Bien que j'apprécie votre approche, il serait utile pour la plupart d'indiquer que vous utilisez Knockout.js dans votre exemple.
connexo
@connexo Cheers, juste mis à jour pour dire exemple en utilisant Knockout.js
YaBCK
0

Pour développer la réponse acceptée pour tous ceux qui utilisent WordPress et GravityForms pour générer leurs formulaires et souhaitent renseigner automatiquement les champs de case à cocher avec une liste de publications et leur vignette en vedette associée

// Change '2' to your form ID
add_filter( 'gform_pre_render_2', 'populate_checkbox' );
add_filter( 'gform_pre_validation_2', 'populate_checkbox' );
add_filter( 'gform_pre_submission_filter_2', 'populate_checkbox' );
add_filter( 'gform_admin_pre_render_2', 'populate_checkbox' );

function populate_checkbox( $form ) {

    foreach( $form['fields'] as &$field )  {

        // Change '41' to your checkbox field ID
        $field_id = 41;
        if ( $field->id != $field_id ) {
            continue;
        }

        // Adjust $args for your post type
        $args = array(
                'post_type' => 'pet',
                'post_status' => 'publish',
                'posts_per_page' => -1,
                'tax_query' => array(
                        array(
                                'taxonomy' => 'pet_category',
                                'field' => 'slug',
                                'terms' => 'cat'
                        )
                )
        );

        $posts = get_posts( $args );

        $input_id = 1;

        foreach( $posts as $post ) {

            $feat_image_url = wp_get_attachment_image( get_post_thumbnail_id( $post->ID ), 'thumbnail' );
            $feat_image_url .= '<br />' . $post->post_title;

            if ( $input_id % 10 == 0 ) {
                $input_id++;
            }

            $choices[] = array( 'text' => $feat_image_url, 'value' => $post->post_title );
            $inputs[] = array( 'label' => $post->post_title, 'id' => "{$field_id}.{$input_id}" );

            $input_id++;
        }

        $field->choices = $choices;
        $field->inputs = $inputs;

    }

    return $form;
}

Et le CSS:

.gform_wrapper .gfield_checkbox li[class^="gchoice_2_41_"] {
    display: inline-block;
}

.gform_wrapper .gfield_checkbox li input[type="checkbox"][id^="choice_2_41_"] {
    display: none;
}


.gform_wrapper .gfield_checkbox li label[id^="label_2_41_"] {
    border: 1px solid #fff;
    padding: 10px;
    display: block;
    position: relative;
    margin: 10px;
    cursor: pointer;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

label[id^="label_2_41_"]:before {
    font-family: "font-icons";
    font-size: 32px;
    color: #1abc9c;
    content: " ";
    display: block;
    background-color: transparent;
    position: absolute;
    top: -5px;
    left: -5px;
    width: 25px;
    height: 25px;
    text-align: center;
    line-height: 28px;
    transition-duration: 0.4s;
    transform: scale(0);
}

label[id^="label_2_41_"] img {
    transition-duration: 0.2s;
    transform-origin: 50% 50%;
}

:checked + label[id^="label_2_41_"] {
    border-color: #ddd;
}

/* FontAwesome tick */
:checked + label[id^="label_2_41_"]:before {
    content: "\e6c8";
    background-color: transparent;
    transform: scale(1);
}

:checked + label[id^="label_2_41_"] img {
    transform: scale(0.9);
    box-shadow: 0 0 5px #333;
    z-index: 0;
}
essexboyracer
la source