Użyj obrazów, takich jak pola wyboru

165

Chciałbym mieć alternatywę dla standardowego pola wyboru - w zasadzie chciałbym używać obrazów, a gdy użytkownik kliknie obraz, wygaszam go i nakładam pole wyboru.

Krótko mówiąc, chcę zrobić coś takiego, jak Recaptcha 2, gdy powoduje to kliknięcie obrazów spełniających określone kryteria. Możesz zobaczyć tutaj demo Recaptcha, ale czasami może to spowodować rozwiązanie pytań tekstowych, w przeciwieństwie do wyboru obrazu. Oto zrzut ekranu:

Zrzut ekranu Google Recaptcha

Po kliknięciu jednego z obrazów (w tym przypadku zawierającego obrazek steku), obraz, który klikasz, zmniejsza się i pojawia się niebieski znacznik wskazujący, że został on zaznaczony.

Powiedzmy, że chcę odtworzyć dokładnie ten przykład.

Zdaję sobie sprawę, że mogę mieć 9 ukrytych pól wyboru i dołączam trochę jQuery, aby po kliknięciu obrazu zaznaczało / odznaczało ukryte pole wyboru. Ale co z kurczeniem się obrazu / nakładaniem kleszcza?

bgs264
źródło
2
możesz mieć dwa identyczne obrazy: jeden z zaznaczeniem, a drugi bez kleszcza. I zmieniaj obrazy po kliknięciu
Alex,
23
@Alex To byłoby szczególnie nieelastyczne rozwiązanie.
Siguza,
2
Czy próbowałeś dodać / usunąć klasę CSS po kliknięciu, która zastępuje rozmiar obrazu i działa :beforemagicznie na obraz zaznaczenia?
Hexaholic
3
@Alex „Nieelastyczny” w tym sensie, że zmiana czegokolwiek wymagałaby dużo więcej pracy, na przykład dodanie nowego zdjęcia do kolekcji lub zmiana ikony z zaznaczeniem, podczas gdy nie miałoby to miejsca, gdyby efekt został utworzony programowo w runtime.
Siguza,
2
@Christian Zastąpienie pól wyboru czymś niestandardowym jest popularną decyzją projektową (nawet jeśli jest to tylko stylowe pole wyboru), a znalezienie dobrych / prostych sposobów na zrobienie tego jest zdecydowanie interesujące dla programistów, którzy mieli z tym do czynienia. I to pytanie (nałożenie zamiany pola wyboru na coś innego) zdecydowanie czyni je interesującym. W szczególności zwróć uwagę, że zaakceptowana odpowiedź w ogóle nie wykorzystuje JS, pomimo pozornej złożoności zadania
Izkata

Odpowiedzi:

309

Czysto semantyczne rozwiązanie HTML / CSS

Jest to łatwe do wdrożenia we własnym zakresie, nie jest konieczne żadne gotowe rozwiązanie. Również wiele Cię nauczy, ponieważ nie wydajesz się zbyt łatwy w CSS.

Oto, co musisz zrobić:

Twoje pola wyboru muszą mieć różne idatrybuty. Pozwala to na podłączenie <label>do niego przy użyciu for-atrybut etykiety .

Przykład:

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

Dołączenie etykiety do pola wyboru wywoła zachowanie przeglądarki: za każdym razem, gdy ktoś kliknie etykietę (lub znajdujący się w niej obraz), pole wyboru zostanie przełączone.

Następnie ukryjesz to pole wyboru, przykładając się display: none;do niego na przykład .

Teraz pozostaje tylko ustawić żądany styl dla swojego label::beforepseudoelementu (który będzie używany jako wizualne elementy zastępujące pole wyboru):

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

Ostatnim trudnym krokiem jest użycie :checkedpseudoselektora CSS do zmiany obrazu, gdy pole wyboru jest zaznaczone:

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

+( Sąsiaduje selektor rodzeństwa ) sprawi, że tylko zmiana etykiety, które bezpośrednio śledzić ukryte pole wyboru w znacznikach.

Możesz to zoptymalizować, umieszczając oba obrazy w mapie spritem i stosując tylko zmianę background-positionzamiast zamiany obrazu.

Oczywiście musisz prawidłowo display: block;ustawić etykietę oraz nałożyć i ustawić prawidłowe widthi height.

Edytować:

Przykład codepen i fragment kodu, który utworzyłem po tych instrukcjach, używają tej samej techniki, ale zamiast używać obrazów dla pól wyboru, zamiany pól wyboru są wykonywane wyłącznie za pomocą CSS , tworząc ::beforena etykiecie, którą po zaznaczeniu ma content: "✓";. Dodaj zaokrąglone krawędzie i słodkie przejścia, a wynik będzie naprawdę przyjemny!

Oto działający kod, który prezentuje technikę i nie wymaga obrazów dla pola wyboru:

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

Oto ten sam kod we fragmencie:

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
źródło
32
Cieszę się że to lubisz! Zacznij więcej bawić się CSS, to dużo zabawy, a CSS może zrobić o wiele więcej, niż można by się spodziewać (poza centrowaniem w pionie, to po prostu zbyt trudne;)).
connexo
1
Tak, a zrozumienie tego, jak to działa, sprawi, że będziesz myśleć w przyszłości, za każdym razem, gdy będziesz potrzebować akcji DOM po kliknięciu. A myślałeś, że możesz zareagować tylko na :hover:)
connexo
To będzie moja następna rzecz do obejrzenia w Pluralsight - trochę CSS :)
bgs264
6
To jest bardzo miłe! Zauważyłem mały problem po dwukrotnym kliknięciu tej samej etykiety, wybiera obraz. To rozwiązuje ten problem: codepen.io/anon/pen/jPmNjg
Ayesh K
1
Tick ​​wyglądałby lepiej jako SVG. Zawartość CSS stawia Cię na łasce strasznych czcionek systemowych.
Adria
31

Czyste rozwiązanie CSS

Wywołane są trzy zgrabne urządzenia:

  1. :checkedselektor
  2. ::beforePseudo selektor
  3. Właściwość css content.

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
źródło
Jak można dodać zachowanie przycisku radiowego do tych pól wyboru? Chodzi mi o to, że po kliknięciu obrazu już wybrany obraz zostanie odznaczony.
Libertad
Ktokolwiek zadaje sobie to samo pytanie, co @Libertad, po prostu zmienia typ wejść na radio i nadaje im taką samą nazwę „atrybut”, <input type="radio" name="myRadio" id="myRadio1" />także zmienia swój css, aby ukryć punktor:input[type=radio]{display: none;}
Lou
8

Zobacz tę wtyczkę jQuery: imgCheckbox (na npm i bower )

Zastrzeżenie: do rozwiązania tego problemu nie jest potrzebny skrypt javascript. Napięcie występuje między łatwością konserwacji a wydajnością kodu. Chociaż nie jest potrzebna wtyczka (ani żaden javascript), z pewnością przyspiesza tworzenie i często ułatwia zmianę.

Rozwiązanie Barebones:

Z bardzo prostym HTML (bez bałaganu z polami wyboru i etykietami itp.):

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

Możesz użyć toggleClass jQuery, aby włączyć / wyłączyć a selectedlub checkedklasę w clickzdarzeniu:

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

Zaznaczone elementy są pobierane za pomocą

$(".checked")

Plus Coolness:

Możesz stylizować obrazy na podstawie tego, ale dużym problemem jest to, że bez innych elementów DOM nie możesz nawet używać ::beforei ::afterdodawać rzeczy takie jak znaczniki wyboru. Rozwiązaniem jest owinięcie obrazów innym elementem (i sensowne jest również dołączenie odbiornika kliknięć do opakowanego elementu).

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

Dzięki temu Twój kod HTML jest naprawdę czysty, a plik js niewiarygodnie czytelny. Spójrz na fragment ...

Korzystanie z wtyczki imgCheckbox jQuery:

Zainspirowany powyższym rozwiązaniem zbudowałem wtyczkę, której można używać równie łatwo jak:

$("img").imgCheckbox();
  • Wstawia dane zaznaczonych obrazów do formularza
  • Obsługuje niestandardowe znaczniki wyboru
  • Obsługuje dostosowany CSS
  • Obsługuje wstępnie wybrane elementy
  • Obsługuje grupy radiowe zamiast prostego przełączania obrazów
  • Zawiera wywołania zwrotne zdarzeń
  • Rozsądne wartości domyślne
  • Lekki i super łatwy w użyciu

Zobacz to w akcji (i zobacz źródło )

jcuenod
źródło
To podejście też mi się podoba, ale ma ono kilka wad, o których warto wspomnieć, z których największą jest to: Zakładając, że użytkownik ma wybierać obrazy, informacje podane przez użytkownika w ten sposób z pewnością mają być w jakiś sposób przetworzone, za pośrednictwem połączenia AJAX lub przesłania formularza. Dla celów dalszego przetwarzania informacji podanych przez użytkownika, posiadanie pól wyboru, w których wiele wyborów musi być odzwierciedlonych i przetworzonych, jest naturalnym, semantycznym sposobem na zrobienie tego.
connexo
To prawda, jest na mojej liście rzeczy do zrobienia dla wtyczki - aby przejąć przesyłanie formularza i wstrzyknąć dane do formularza. Ale masz rację, pola wyboru są semantyczne dla formularzy
jcuenod
Próbowałem użyć twojej wtyczki, ale jeśli dodam więcej obrazów po inicjalizacji, wydaje się, że nie ma sposobu na zniszczenie poprzedniej instancji, aby można było dołączyć nowe obrazy
Ian Kim
@IanKim ciekawy przypadek użycia, czy możesz wyjaśnić więcej o tym, co próbujesz zrobić w numerze na github? github.com/jcuenod/imgCheckbox
jcuenod
To niesamowita wtyczka! proszę opublikować Webjar =)
naXa
6

Dodałbym dodatkowy element div z position: relative;i, class="checked"który ma taką samą szerokość / wysokość jak obraz i pozycję w left: 0; top: 0;zawierającym ikonę. Zaczyna się od display: none;.

Teraz możesz posłuchać clickzdarzenia:

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

W ten sposób możesz uzyskać ikonę, a także zmienić rozmiar obrazu na mniejszy.

Uwaga: Chciałem tylko pokazać "logikę" stojącą za moimi myślami, ten przykład może nie działać lub zawiera pewne błędy.

Cagatay Ulubay
źródło
Technika, którą pokazałem jest tak prosta i solidna, że ​​jest nawet często stosowana w sytuacjach, w których nie ma semantycznie żadnych pól wyboru, takich jak przełączanie wyświetlania innych elementów i tym podobne. Jest to przypadek, w którym jest to nie tylko najłatwiejsze i najbardziej elastyczne rozwiązanie, ale także najlepsze semantycznie.
connexo
2
Tak, masz rację. Na początku chciałem wskazać twoją pierwszą odpowiedź i poprawić ją (przed edycją), ponieważ lubię No-JS i fajne sposoby semantyczne, ale potem przeczytałem, że OP chce czegoś w jQuery, więc zmieniłem odpowiedź. Pamiętam, gdzie dostałem głos przeciw, ponieważ OP nie chciał „lepszego” lub „właściwego sposobu” rozwiązania, ale dokładnie tak, jak napisał. Może jest po prostu coś, co przeczytałem lub nie zrozumiałem, albo zależy to od OP?
Cagatay Ulubay
2

Zauważyłem, że inne odpowiedzi albo nie używają <label>(dlaczego nie?), Albo wymagają dopasowania fori idatrybutów. Oznacza to, że jeśli masz sprzeczne identyfikatory, twój kod nie będzie działał i musisz pamiętać, aby za każdym razem tworzyć unikalne identyfikatory.

Ponadto, jeśli ukryjesz za inputpomocą display:nonelub visibility:hidden, przeglądarka nie skupi się na nim.

Pole wyboru i jego tekst (lub w tym przypadku obraz) można umieścić w etykiecie:

.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
źródło
1

Oto szybki przykład wyboru obrazu, takiego jak pole wyboru

Zaktualizowany przykład przy użyciu 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
źródło
1
Chociaż podoba mi się twoje podejście, dla większości pomocne byłoby stwierdzenie, że w swoim przykładzie używasz Knockout.js.
connexo
@connexo Cheers, właśnie zaktualizowano, by
podać
0

Aby rozwinąć zaakceptowaną odpowiedź dla każdego, kto używa WordPress i GravityForms do generowania swoich formularzy i chce automatycznie wypełniać pola wyboru listą postów i powiązaną z nimi miniaturą

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

Oraz 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
źródło