Uncheck the switch after pressing another button

I am creating a voting system with two buttons (upvote / downvote). I used input tags and labels to create this, but there is a problem:

If I use radio buttons, I can either vote up or down, but I cannot vote.

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

What I want to achieve is the ability to only vote up or down and also remove the vote (think reddit).

I searched thoroughly for an answer and I came to the conclusion that this cannot be done with CSS. There are many scripts out there, the problem is that 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 to disable the downvote button when I click on upvote and vice versa, but again, I have no idea what this code would look like or how to implement it.

Maybe there is a simpler solution using pure CSS to change the appearance of another button even after it has been tested? I don't know, but I would rather use a solution like this 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>
      

Run code


+3


source to share


2 answers


If you only want to use a CSS solution, you can try adding a hidden default third option that won't vote.

Then, when the user raises or decreases, the third parameter is displayed and overrides the selected option.

Therefore, when the user thinks they are clicking on the selected option again, they actually select the option without voting.

#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>
      

Run code


With minor modifications, it can also be keyboard accessible:



#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>
      

Run code


The snippets above use borders to simulate triangles / arrows. Background images can be used in the same way.

#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>
      

Run code


+6


source


You have a pure CSS solution, here's the javascript version. It places a click listener on the parent of the voting buttons. When the button is pressed and checked, it disables other voting buttons in the same container, so only one vote is checked at a time. It will work with any number of voting buttons (you could say +1, +2, etc.).

If clicking on a checkbox cancels it, for example when canceling a vote, the function does nothing. Make sure the script is placed after the container or the load event is used. The listener can be attached to as many containers as possible, since each contains only 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 should work in IE 8. If it needs to work in older browsers it can be done quite easily, especially if there is only one set of voting buttons on the page (it requires a little line modification var els = ...

).

Label elements don't need an identifier for the script.

0


source







All Articles