Reputation: 167
In my Meteor project, I have two buttons. One button is an upvote
button, which adds a point to the score of an entry, while the other is a downvote
button, which does the opposite. My site doesn't require login.
How can I restrict it so that any given device can only click either the upvote
or downvote
button initially, and then if that device decides to change it's vote, it should be able to click only the other button, and so on?
Upvotes: 2
Views: 206
Reputation: 2677
You can use HTML 5 LocalStorage. But it will only work on latest browsers. If you want suppoert for old browsers as well then you might be interested in this question as well. If your user base doesn't use very old browsers then you can do it with LocalStorage like this,
In template's created callback,
Template.yourTemplate.created = function () {
var template = this;
var userVote = null;
if(typeof(Storage) !== "undefined") {
userVote = localStorage.getItem("userVote");
}
template.userVote = new ReactiveVar(userVote); //or you can use Session.setDefault("userVote", userVote)
}
When user clicks on the up or down button
Template.yourTemplate.events({
'click #upButton': function (ev, template) {
localStorage.setItem("userVote", "up");
template.userVote.set("up"); // or Session.set("userVote", "up");
},
'click #downButton': function (ev, template) {
localStorage.setItem("userVote", "down");
template.userVote.set("down"); // or Session.set("userVote", "down");
}
});
Then to disable buttons, you can do something like this in your helpers,
Template.yourTemplate.helpers({
'isUpButtonDisabled': function () {
var template = Template.instance();
var userVote = template.userVote.get(); // or Session.get("userVote");
return userVote === "up";
},
'isDownButtonDisabled': function (ev, template) {
var template = Template.instance();
var userVote = template.userVote.get(); // or Session.get("userVote");
return userVote === "down";
}
});
Update: This answer uses localStorage
so that the application can keep track of the user's vote even when user visits the same site at a later date, which was what OP was trying to do, since user can vote without a login.
EDIT: Based on your comment to have different votes for different templates/topics. Assuming you have current topic's id in template's current data. You can do something like this,
In template's created callback,
Template.yourTemplate.created = function () {
var template = this;
template.userVote = new ReactiveVar(null); //or you can use Session.setDefault("userVote", null)
template.autorun(function () {
var data = Template.currentData();
var topicId = data.topicId;
var userVote = null;
if(typeof(Storage) !== "undefined") {
userVote = localStorage.getItem("userVote" + topicId);
}
template.userVote.set(userVote); //or you can use Session.set("userVote", userVote);
});
}
When user clicks on the up or down button
Template.yourTemplate.events({
'click #upButton': function (ev, template) {
var topicId = this.topicId;
localStorage.setItem("userVote" + topicId, "up");
template.userVote.set("up"); // or Session.set("userVote", "up");
},
'click #downButton': function (ev, template) {
var topicId = this.topicId;
localStorage.setItem("userVote" + topicId, "down");
template.userVote.set("down"); // or Session.set("userVote", "down");
}
});
Then to disable buttons, you can do something like this in your helpers,
Template.yourTemplate.helpers({
'isUpButtonDisabled': function () {
var template = Template.instance();
var userVote = template.userVote.get(); // or Session.get("userVote");
return userVote === "up";
},
'isDownButtonDisabled': function (ev, template) {
var template = Template.instance();
var userVote = template.userVote.get(); // or Session.get("userVote");
return userVote === "down";
}
});
Upvotes: 2
Reputation: 44098
It sounds like a regular old radio button should do the trick.
I made some fancier stuff as well, see this CodePen.
Update
Added @4castle's rescind vote function. Nice touch.
Update 2
Per OP's request, the radio buttons are now the arrows.
html,
body {
box-sizing: border-box;
background: #111;
color: #DDD;
font: 400 16px/1.4'Verdana';
height: 100vh;
width: 100vw;
}
*,
*:before,
*:after {
box-sizing: inherit;
margin: 0;
padding: 0;
border: 0 none hlsa(0%, 0, 0, 0);
outline: 0 none hlsa(0%, 0, 0, 0);
}
fieldset {
margin: 0 1em 1em 1em;
padding: 8px;
border-radius: 9px;
border: 3px double #FF8;
width: 100%;
max-width: 19em;
}
legend {
font: small-caps 700 1.5rem/2"Palatino Linotype";
color: #FD1;
}
/* RadZ */
#radz input.chkrad {
display: none;
}
#radz input.chkrad + label {
color: #EEE;
background: transparent;
font-size: 16px;
}
#radz input.chkrad:checked + label {
color: #0ff;
background: transparent;
font-size: 16px;
}
#radz input.chkrad + label span {
display: inline-block;
width: 18px;
height: 18px;
margin: -1px 15px 0 0;
vertical-align: baseline;
cursor: pointer;
}
#radz input + label span {
background: transparent;
line-height: 100%;
}
input.A + label span:before {
content: '△';
color: #0ff;
font-style: normal;
font-weight: 700;
font-size: 24px;
}
input.A:checked + label span:before {
padding: -5px 15px 5px;
font-size: 16px;
}
input.A:checked + label span:before {
content: '▲';
color: #0ff;
font-style: normal;
font-weight: 700;
font-size: 24px;
}
input.B + label span:before {
content: '▽';
color: #0ff;
font-style: normal;
font-weight: 700;
font-size: 24px;
}
input.B:checked + label span {
padding: -5px 15px 5px;
font-size: 16px;
}
input.B:checked + label span:before {
content: '▼';
color: #0ff;
font-style: normal;
font-weight: 700;
font-size: 24px;
}
input.fade + label span,
input.fade:checked + label span {
-webkit-transition: background 0.7s linear;
-moz-transition: background 0.7s linear;
transition: background 0.7s linear;
}
<fieldset id="radz" name="radz">
<legend>Vote</legend>
<input type='radio' name='rad' id="rad1" class="chkrad A fade" value='1' />
<label for="rad1"><span></span>Up</label>
<br/>
<input type='radio' name='rad' id="rad2" class="chkrad B fade" value='2' />
<label for="rad2"><span></span>Down</label>
<br/>
</fieldset>
// Rescind vote function provided by 4castle
// http://stackoverflow.com/users/5743988/4castle
var selectedRad;
var voteRads = document.querySelectorAll('input[name="vote"]');
for (var i = 0; i < voteRads.length; i++) {
voteRads[i].onclick = function() {
if (selectedRad == this) {
this.checked = false;
selectedRad = null;
} else {
selectedRad = this;
}
};
}
.rad1 + label:after {
content: '△';
}
.rad1:checked + label:after {
content: '▲';
}
.rad2 + label:after {
content: '▽';
}
.rad2:checked + label:after {
content: '▼';
}
<input id="up" type="radio" class="rad1" name="vote">
<label for="up"></label>
<br/>
<label>Vote</label>
<br/>
<input id="down" type="radio" class="rad2" name="vote">
<label for="down"></label>
Upvotes: 2