简体   繁体   中英

Unchecking a radio button after clicking a different one

I'm making a voting system with two buttons (upvote / downvote). I've used input and label tags to create this, but there is a problem:

If I use radio buttons, I can either vote up or down, but I can't un-vote.

If I use checkboxes, I can un-vote, but I can also vote both up and down.

What I want to achieve is being able to only vote up or down while also being able to remove the vote (think reddit).

I've thoroughly looked for an answer and I've come to find that this is not possible to do with CSS. There are a lot of scripts out there, the problem is I have no idea about JavaScript or jQuery, so I don't know how to implement the code.

I think the easiest solution would be unchecking the downvote button when I click on upvote and vice versa, but again, I have no idea how this code would look like or how to implement it.

Maybe there's an easier solution using pure CSS to change the appearance of the other button even after it has been checked? I don't know, but I'd much rather use such a solution if it exists.

My code:

 input[type=radio] { display: none } #up { width: 20px; height: 16px; background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png) left top; position: absolute; left: 50px; top: 50px } #down { width: 20px; height: 16px; background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png) left bottom; opacity: 0.5; position: absolute; left: 50px; top: 84px } #vote { width: 21px; height: 18px; text-align: center; font-size: 26px; line-height: 18px; position: absolute; left: 50px; top: 66px; z-index: -1 } #up:hover { background-position: top; cursor: pointer } #upvote:checked ~ #up { background-position: top; } #upvote:checked ~ #vote { color: #DC5B28; z-index: 1 } #down:hover { background-position: bottom; cursor: pointer; opacity: 1 } #downvote:checked ~ #down { background-position: bottom; opacity: 1 } #downvote:checked ~ #vote { color: #3580DD } #no { position: absolute; display: none; background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png) } #upvote:checked ~ #no { display: block; background-position: left top; left: 50px; top: 50px } #downvote:checked ~ #no { display: block; background-position: left bottom; left: 50px; top: 84px; opacity: 0.5 } 
 <input type="radio" name="vote" value="+1" id="upvote"> <label for="upvote" id="up"></label> <input type="radio" name="vote" value="-1" id="downvote"> <label for="downvote" id="down"></label> <input type="radio" name="vote" value="0" id="novote"> <label for="novote" id="no"></label> <div id="vote">•</div> 

If you want a CSS-only solution, you can try adding a default hidden third option, which represents no vote.

Then, when user upvotes or downvotes, that third option is shown, and it overlaps the chosen option.

Therefore, when the user thinks he is clicking the chosen option again, he is in fact choosing the no vote option.

 #vote { position: relative; } #vote > input { display: none; /* Hide radio buttons */ } #vote > label { cursor: pointer; display: block; width: 0; border: 50px solid; /* We will make them look like... */ border-color: black transparent; /* ...triangles using borders */ } #upvote + label { border-top: none; /* Triangulating */ } #downvote + label { border-bottom: none; /* Triangulating */ margin-top: 15px; /* Space between triangles */ } #vote > input:checked + label { border-color: orange transparent; /* Highlight chosen option */ } #vote > #novote-label { display: none; /* Hide it by default */ position: absolute; /* Take it out of the normal flow */ border-top: none; border-color: transparent; } #upvote:checked ~ #novote-label { /* Display it overlapping upvote */ display: block; top: 0; } #downvote:checked ~ #novote-label { /* Display it overlapping downvote */ display: block; bottom: 0; } 
 <div id="vote"> <input type="radio" name="vote" value="+1" id="upvote" /> <label for="upvote"></label> <input type="radio" name="vote" value="-1" id="downvote" /> <label for="downvote"></label> <input type="radio" name="vote" value="0" id="novote" /> <label for="novote" id="novote-label"></label> </div> 

With few changes it can be keyboard accessible too:

 #vote { position: relative; } #vote > input { /* Hiding in a keyboard-accesible way */ opacity: 0; position: absolute; z-index: -1; } #vote > input:focus + label { outline: 1px dotted #999; /* Keyboard friendly */ } #vote > label { cursor: pointer; display: block; width: 0; border: 50px solid; /* We will make them look like... */ border-color: black transparent; /* ...triangles using borders */ } #upvote + label { border-top: none; /* Triangulating */ } #downvote + label { border-bottom: none; /* Triangulating */ margin-top: 15px; /* Space between triangles */ } #vote > input:checked + label { border-color: orange transparent; /* Highlight chosen option */ } #vote > #novote-label { display: none; /* Hide it by default */ position: absolute; /* Take it out of the normal flow */ border-top: none; border-color: transparent; } #upvote:checked ~ #novote-label { /* Display it overlapping upvote */ display: block; top: 0; } #downvote:checked ~ #novote-label { /* Display it overlapping downvote */ display: block; bottom: 0; } 
 <div id="vote"> <input type="radio" name="vote" value="+1" id="upvote" /> <label for="upvote"></label> <input type="radio" name="vote" value="-1" id="downvote" /> <label for="downvote"></label> <input type="radio" name="vote" value="0" id="novote" /> <label for="novote" id="novote-label"></label> </div> 

The snippets above uses borders to simulate triangles/arrows. Similarly, background images can be used too.

 #vote { position: relative; } #vote > input { display: none; } #vote > label { cursor: pointer; display: block; width: 20px; height: 16px; background-image: url('http://i.stack.imgur.com/36F2b.png'); } #vote > #up { background-position: left top; } #vote > #down { background-position: left bottom; } #upvote:checked ~ #up { background-position: top; } #downvote:checked ~ #down { background-position: bottom; } #vote > #no { display: none; position: absolute; background: none; } #upvote:checked ~ #no { display: block; top: 0; } #downvote:checked ~ #no { display: block; bottom: 0; } 
 <div id="vote"> <input type="radio" name="vote" value="+1" id="upvote" /> <label for="upvote" id="up"></label> <input type="radio" name="vote" value="-1" id="downvote" /> <label for="downvote" id="down"></label> <input type="radio" name="vote" value="0" id="novote" /> <label for="novote" id="no"></label> </div> 

You have a pure CSS solution, here's a javascript version. It puts a click listener on an ancestor of the "vote" buttons. When a button is clicked and checked, it unchecks the other vote buttons in the same container so only one vote is checked at a time. It will work with any number of vote buttons (you might have say +1, +2, etc.).

If clicking on the checkbox unchecks it, eg when cancelling a vote, the function does nothing. Make sure the script is placed after the container, or use the load event. The listener can be attached to as many containers as you like, just as long as each only contains one set of buttons.

<!-- A container for the vote buttons -->
<div id="voteContainer">
  <input type="checkbox" name="vote" value="+1" id="upvote">
  <label for="upvote" id="up">Up</label>
  <br>
  <input type="checkbox" name="vote" value="-1" id="downvote">
  <label for="downvote" id="down">Down</label>
</div>

<script>
(function() {
  var div = document.getElementById('voteContainer');
  if (div) div.onclick = setVote;

  // Looks at the state of els
  function setVote(event) {
    event = event || window.event;
    var target = event.target || event.srcElement;


    // If target checkbox is checked, uncheck all others
    if (target.checked) {
      var els = this.querySelectorAll('input[name=' + target.name + ']')

      for (var i=0; i<els.length; i++) {
        els[i].checked = els[i] === target;
      }
    }
  }
}());
</script>

The above should work in all modern browsers and IE back to IE 8. If it needs to work in older browsers, that can be done pretty easily, especially if there is only one set of vote buttons in the page (it requires a small modification to the var els = ... line).

The label elements don't need an ID for the script to work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM