Reputation: 487
I'm making a game and using JavaScript/jQuery to spend points of character's attributes. I have six inputs like this one, each representing one attribute:
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
And one input like this (the "pool"):
<input id="pool" type="number" min="0" max="36" step="1" value="36" readonly>
The last input functions as a "pool", which means that it is the total of remaining points to be spent on the group of the six inputs. I would like to have a following:
I've tried different approaches, but couldn't prevent player from exceeding the values beyond the pool maximum, mostly because I don't know how to prevent the change event.
This is what I've tried so far:
<script>
let attributeVal = $(".attributeVal");
attributeVal.on("change paste keyup", function (e) {
let prev = $(this).data('val');
let maxPointsOverall = 42;
let spentPoints = 0;
let poolInput = $('#poolInput');
$(".attributeVal").each(function () {
spentPoints += +$(this).val();
});
if ((maxPointsOverall - spentPoints) < 0) {
$(this).val(prev);
poolInput.val(0);
} else {
poolInput.val(42 - spentPoints);
}
});
attributeVal.on("cut copy paste", function (e) {
e.preventDefault();
});
</script>
Could you please give me any hints, how I can make this one work?
let attributeVal = $(".attributeVal");
attributeVal.on("change paste keyup", function(e) {
let prev = $(this).data('val');
let maxPointsOverall = 42;
let spentPoints = 0;
let poolInput = $('#poolInput');
$(".attributeVal").each(function() {
spentPoints += +$(this).val();
});
if ((maxPointsOverall - spentPoints) < 0) {
$(this).val(prev);
poolInput.val(0);
} else {
poolInput.val(42 - spentPoints);
}
});
attributeVal.on("cut copy paste", function(e) {
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label class="font-weight-bold unselectable mb-3">Points available
<input id="poolInput" type="number" min="0" max="36" step="1" value="36" readonly>
</label>
<div class="form-group">
<label>Strength
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Endurance
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Dexterity
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Perception
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Intelligence
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Charisma
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
Here is JSFiddle to illustrate the problem
Upvotes: 1
Views: 44
Reputation: 370679
An issue is that, once the input event has been fired, you can't reset it to the previous number without saving the state of every input somewhere, which is a bit messy. You might consider first validating the value:
maxPointsOverall
. (If it overflows, reduce the current value to the point such that pointsSpentOnOtherAttribs + newVal === maxPointsOverall
)Then set the current value to the validated newVal
, and calculate / populate the value for the #poolInput
.
To make sure the inputs get fixed immediately, rather than on a short delay, add an input
listener (which will fire around keypress, rather than on keyup):
const attributeVal = $(".attributeVal");
attributeVal.on("change paste keyup input", function(e) {
let newVal = Math.floor($(this).val());
newVal = Math.max(1, newVal);
newVal = Math.min(36, newVal);
const maxPointsOverall = 42;
let pointsSpentOnOtherAttribs = 0;
$(".attributeVal").not(this).each(function() {
pointsSpentOnOtherAttribs += +$(this).val();
});
if (newVal + pointsSpentOnOtherAttribs > maxPointsOverall) {
// Invalid, reset these points to the maximum allowable:
newVal = maxPointsOverall - pointsSpentOnOtherAttribs;
}
// New value has been validated, put it into the DOM:
$(this).val(newVal);
const pointsLeft = maxPointsOverall - (pointsSpentOnOtherAttribs + newVal);
$('#poolInput').val(pointsLeft);
});
attributeVal.on("cut copy paste", function(e) {
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label class="font-weight-bold unselectable mb-3">Points available
<input id="poolInput" type="number" min="0" max="36" step="1" value="36" readonly>
</label>
<div class="form-group">
<label>Strength
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Endurance
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Dexterity
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Perception
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Intelligence
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
<div class="form-group">
<label>Charisma
<input class="attributeVal" type="number" min="1" max="36" step="1" value="1">
</label>
</div>
Upvotes: 1