Use imagens como caixas de seleção

164

Eu gostaria de ter uma alternativa a uma caixa de seleção padrão - basicamente eu gostaria de usar imagens e, quando o usuário clicar na imagem, esmaecer e sobrepor uma caixa de seleção.

Em essência, quero fazer algo como o Recaptcha 2 faz quando você clica nas imagens que atendem a um determinado critério. Você pode ver uma demonstração do Recaptcha aqui, mas às vezes você pode resolver questões de texto, em oposição à seleção de imagens. Então, aqui está uma captura de tela:

Captura de tela do Google Recaptcha

Quando você clica em uma das imagens (nesse caso, contendo uma foto de bife), a imagem em que você clica diminui de tamanho e o tique azul aparece, indicando que você o marcou.

Digamos que eu queira reproduzir este exemplo exato.

Sei que posso ter 9 caixas de seleção ocultas e anexar alguns jQuery para que, quando clico na imagem, ela marque / desmarque a caixa de seleção oculta. Mas e quanto à redução da imagem / sobreposição do tiquetaque?

bgs264
fonte
2
você pode ter duas imagens idênticas: uma com marca e outra sem marca. E altere imagens ao clicar
Alex
22
@ Alex Essa seria uma solução particularmente flexível.
Siguza
2
Você já tentou adicionar / remover uma classe CSS ao clicar que substitui o tamanho da imagem e faz alguma :beforemágica para a imagem do tick?
Hexaholic
3
@Alex "Unflexible" no sentido de que exigiria muito mais trabalho para mudar qualquer coisa, como adicionar uma nova imagem à coleção ou alterar o ícone "marcado", quando não seria esse o caso se o efeito fosse criado programaticamente em tempo de execução.
Siguza 5/06
2
@Christian Substituir caixas de seleção por algo personalizado é uma decisão de design popular (mesmo que seja apenas uma caixa de seleção com estilo), e encontrar maneiras boas / simples de fazer isso é definitivamente de interesse para os desenvolvedores que tiveram que lidar com isso. E essa pergunta (sobrepor a substituição da caixa de seleção em outra coisa) definitivamente a torna interessante. Em particular, nota como a resposta aceita não usa JS em tudo, apesar da aparente complexidade da tarefa
Izkata

Respostas:

308

Solução HTML / CSS semântica pura

É fácil de implementar por conta própria, não é necessária uma solução pré-fabricada. Também lhe ensinará muito, pois você não parece muito fácil com CSS.

Isso é o que você precisa fazer:

Suas caixas de seleção precisam ter idatributos distintos . Isso permite conectar um <label>a ele, usando o foratributo- label .

Exemplo:

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

Anexar o rótulo à caixa de seleção acionará o comportamento do navegador: sempre que alguém clicar no rótulo (ou na imagem dentro dele), a caixa de seleção será alternada.

Em seguida, oculte a caixa de seleção aplicando, por exemplo, display: none;a ela.

Agora, basta definir o estilo desejado para o seu label::beforepseudo elemento (que será usado como elemento de substituição visual da caixa de seleção):

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

Em uma última etapa complicada, você usa o :checkedpseudo-seletor do CSS para alterar a imagem quando a caixa de seleção está marcada:

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

O +( seletor de irmão adjacente ) garante que você altere apenas os rótulos que seguem diretamente a caixa de seleção oculta na marcação.

Você pode otimizar isso colocando as duas imagens em um spritemap e aplicando apenas uma alteração em background-positionvez de trocar a imagem.

Obviamente, você precisa posicionar o rótulo corretamente, aplicar display: block;e definir o correto widthe height.

Editar:

O exemplo e o snippet do codepen, que criei após essas instruções, usam a mesma técnica, mas , em vez de usar imagens para as caixas de seleção, as substituições das caixas de seleção são feitas exclusivamente com CSS , criando um ::beforeno rótulo que, uma vez marcado, possui content: "✓";. Adicione algumas bordas arredondadas e transições doces e o resultado é realmente agradável!

Aqui está um código de trabalho que mostra a técnica e não requer imagens para a caixa de seleção:

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

Aqui está o mesmo código em um trecho:

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
fonte
32
Estou feliz por ter gostado! Comece a mexer mais com o CSS, é muito divertido e o CSS pode fazer muito mais do que você provavelmente esperaria (exceto a centralização vertical, isso é muito difícil;)).
connexo
1
Sim, e entender o modo como funciona fará você pensar no futuro, sempre que desejar alguma ação do DOM ao clicar. E você pensou que você pode reagir apenas para :hover:)
connexo
Isso vai ser minha próxima coisa para assistir em Pluralsight - algumas coisas CSS :)
bgs264
6
Isso é muito legal! Percebi um pequeno problema quando você clica no mesmo rótulo duas vezes, ele seleciona a imagem. Isso corrige: codepen.io/anon/pen/jPmNjg
Ayesh K
1
Tick ​​teria ficado melhor como SVG. O conteúdo CSS coloca você à mercê de algumas fontes terríveis do sistema.
Adria
31

Solução CSS pura

Existem três dispositivos puros invocados:

  1. O :checkedseletor
  2. O ::beforepseudo-seletor
  3. A contentpropriedade 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
fonte
Como é possível adicionar um comportamento de botão de opção a essas caixas de seleção? Quero dizer, quando você clica em uma imagem, a imagem já selecionada fica desmarcada.
Libertad
Quem faz a mesma pergunta que @Libertad simplesmente altera o tipo de entradas para radio e atribui a todos o mesmo nome "attribute" <input type="radio" name="myRadio" id="myRadio1" />também altera seu css para ocultar a bala:input[type=radio]{display: none;}
Lou
8

Veja este plugin jQuery: imgCheckbox (em npm e bower )

Isenção de responsabilidade: Não é necessário javascript para resolver este problema. A tensão está entre a manutenção e a eficiência do código. Embora não seja necessário um plug-in (ou qualquer javascript), ele certamente torna mais rápido a criação e muitas vezes mais fácil a alteração.

Solução Barebones:

Com HTML muito simples (nada de bagunça com caixas de seleção, rótulos etc.):

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

Você pode usar jQuery toggleClass para ligar / desligar um selectedou checkedclasse no clickevento:

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

Os itens marcados são buscados com

$(".checked")

Mais Frieza:

Você pode estilizar as imagens com base nisso, mas um grande problema é que, sem outros elementos DOM, você não pode nem usar ::beforee ::afteradicionar itens como marcas de seleção. A solução é agrupar suas imagens com outro elemento (e faz sentido anexar o ouvinte de clique ao elemento agrupado).

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

Isso deixa seu html realmente limpo e seu js incrivelmente legível. Dê uma olhada no trecho ...

Usando o plug-in jQuery imgCheckbox :

Inspirado na solução acima, criei um plugin que pode ser usado tão facilmente quanto:

$("img").imgCheckbox();
  • Injeta os dados das imagens verificadas no seu formulário
  • Suporta marcas de seleção personalizadas
  • Suporta CSS personalizado
  • Suporta elementos pré-selecionados
  • Suporta grupos de rádio em vez de simples alternância de imagens
  • Possui retornos de chamada de evento
  • Padrões sensíveis
  • Leve e super fácil de usar

Veja em ação (e veja a fonte )

jcuenod
fonte
Também gosto dessa abordagem, mas há várias desvantagens que merecem ser mencionadas, sendo a maior delas: supondo que o usuário tenha a intenção de escolher imagens, as informações fornecidas pelo usuário dessa maneira certamente devem ser processadas de alguma forma. através de uma chamada AJAX ou através de um envio de formulário. Para os propósitos de processamento adicional das informações fornecidas pelo usuário, ter caixas de seleção nas quais várias opções devem ser refletidas e processadas é apenas a maneira natural e semântica de fazê-lo.
connexo
Isso é verdade, está na minha lista de tarefas do plug-in - seqüestrar o envio de formulários e injetar os dados no formulário. Mas você está certo, caixas de seleção são semântico para formas
jcuenod
Eu tentei usar o plugin, mas se eu estou adicionando mais imagens após a inicialização não parece ser uma maneira de destruir a instância anterior de modo que as novas imagens podem ser incluídos
Ian Kim
Caso de uso interessante do @IanKim, você pode explicar mais sobre o que está tentando fazer em um problema no github? github.com/jcuenod/imgCheckbox
jcuenod
É um plugin incrível! publique um Webjar =)
naXa
6

Eu acrescentaria uma div extra com position: relative;e class="checked"que tenha a mesma largura / altura da imagem e que a posição que left: 0; top: 0;contém o ícone. Começa com display: none;.

Agora você pode ouvir o click-event:

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

Dessa forma, você pode obter o ícone e também redimensionar a imagem para uma maneira menor.

Aviso: Só queria mostrar a "lógica" por trás dos meus pensamentos, este exemplo pode não funcionar ou tem alguns erros.

Cagatay Ulubay
fonte
A técnica que eu demonstrei é tão simples e robusta que é aplicada mesmo frequentemente em situações em que você não tem caixas de seleção semanticamente, como alternar a exibição de outros elementos e coisas do gênero. Este é o caso de uso em que não é apenas a solução mais fácil e flexível, mas também a melhor semanticamente.
connexo
2
Sim, você está certo. No começo, eu queria apontar sua primeira resposta e melhorá-la (antes da edição), porque gosto de No-JS e de boas maneiras semânticas, mas depois que li que o OP quer algo no jQuery, mudei minha resposta. Lembro-me de onde recebi um voto negativo, porque o OP não queria uma solução "melhor" ou "do jeito certo", mas da maneira exata como ele escreveu. Talvez exista apenas algo que eu exagero ou não entendi ou depende do OP?
Cagatay Ulubay
2

Percebi que outras respostas não usam <label>(por que não?) Ou exigem correspondência fore idatributos. Isso significa que, se você tiver IDs conflitantes, seu código não funcionará e lembre-se de criar IDs únicos a cada vez.

Além disso, se você ocultar o inputcom display:noneou visibility:hidden, o navegador não focará nele.

Uma caixa de seleção e seu texto (ou, neste caso, imagem) podem ser agrupados em um rótulo:

.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
fonte
1

Aqui está um exemplo rápido de como selecionar uma imagem como uma caixa de seleção

Exemplo atualizado usando o 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
fonte
1
Embora eu goste da sua abordagem, seria útil para a maioria afirmar que você está usando o Knockout.js no seu exemplo.
connexo
Felicidades do @connexo, atualizadas apenas para dizer o exemplo usando o Knockout.js
YaBCK
0

Expandir a resposta aceita para quem usa WordPress e GravityForms para gerar seus formulários e deseja preencher automaticamente os campos das caixas de seleção com uma lista de postagens e a Miniatura em destaque associada

// 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;
}

E o 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
fonte