Creating a message that pops up at the end of a quiz depending on score - javascript

I hope someone can help me but I have just started learning javascript and I have been working on a quiz for a page of a learning website that I am helping to create. I have been asked to add a message that pops up at the end of the quiz but I can't seem to get it to work. Please excuse any terrible obvious mistakes as like I said I have only been looking into it for a couple of days.
I have a div in the html called message that I wanted to the message to appear.
This is the js I have so far. Any tips would be massively appreciated.
(function($) {
$.fn.emc = function(options) {
var defaults = {
key: [],
scoring: "normal",
progress: true
},
settings = $.extend(defaults,options),
$quizItems = $('[data-quiz-item]'),
$choices = $('[data-choices]'),
itemCount = $quizItems.length,
chosen = [],
$option = null,
$label = null;
emcInit();
if (settings.progress) {
var $bar = $('#emc-progress'),
$inner = $('<div id="emc-progress_inner"></div>'),
$perc = $('<span id="emc-progress_ind">0/'+itemCount+'</span>');
$bar.append($inner).prepend($perc);
}
function emcInit() {
$quizItems.each( function(index,value) {
var $this = $(this),
$choiceEl = $this.find('.choices'),
choices = $choiceEl.data('choices');
for (var i = 0; i < choices.length; i++) {
$option = $('<input name="'+index+'" id="'+index+'_'+i+'" type="radio">');
$label = $('<label for="'+index+'_'+i+'">'+choices[i]+'</label>');
$choiceEl.append($option).append($label);
$option.on( 'change', function() {
return getChosen();
});
}
});
}
function getChosen() {
chosen = [];
$choices.each( function() {
var $inputs = $(this).find('input[type="radio"]');
$inputs.each( function(index,value) {
if($(this).is(':checked')) {
chosen.push(index + 1);
}
});
});
getProgress();
}
function getProgress() {
var prog = (chosen.length / itemCount) * 100 + "%",
$submit = $('#emc-submit');
if (settings.progress) {
$perc.text(chosen.length+'/'+itemCount);
$inner.css({height: prog});
}
if (chosen.length === itemCount) {
$submit.addClass('ready-show');
$submit.click( function(){
return scoreNormal();
});
}
}
function scoreNormal() {
var wrong = [],
score = null,
$scoreEl = $('#emc-score');
for (var i = 0; i < itemCount; i++) {
if (chosen[i] != settings.key[i]) {
wrong.push(i);
}
}
$quizItems.each( function(index) {
var $this = $(this);
if ($.inArray(index, wrong) !== -1 ) {
$this.removeClass('item-correct').addClass('item-incorrect');
} else {
$this.removeClass('item-incorrect').addClass('item-correct');
}
});
score = ((itemCount - wrong.length) / itemCount).toFixed(2) * 100 + "%";
$scoreEl.text("You scored a "+score).addClass('new-score');
}
function print(message) {
document.write(message);
}
if (score===100){
print('congratulations');
}else if(score<=99){
print('Try Again');
}
}
}(jQuery));
$(document).emc({
key: ["1","2","1","1","1","1"]
});

Popup Message
form controls tags
template literal interpolation
nested ternaries
Event Delegation
CSS transform and transition driven by .class
Demo
Enter a number in the <input>. To close the popup message, click the X in the upper righthand corner.
$('#quiz').on('change', function(e) {
var score = parseInt($('#score').val(), 10);
var msg = `Your score is ${score}<sup>×</sup><br>`;
var remark = (score === 100) ? `Perfect, great job!`: (score < 100 && score >= 90) ? `Well done`: (score < 90 && score >= 80) ? `Not bad`: (score < 80 && score >= 70) ? `You can do better`:(score < 70 && score >= 60) ? `That's bad`: `Did you even try?`;
$('#msg legend').html(`${msg}${remark}`).addClass('newScore');
$('#msg legend').on('click', 'sup', function(e) {
$(this).parent().removeClass('newScore');
});
});
#msg legend {
position: absolute;
z-index: 1;
transform: scale(0);
transition: 0.6s;
}
#msg legend.newScore {
font-size:5vw;
text-align:center;
transform-origin: left bottom;
transform: scale(2) translate(0,70%);
transition: 0.8s;
}
#msg legend.newScore sup {
cursor:pointer
}
<form id='quiz'>
<fieldset id='msg'>
<legend></legend>
<input id='score' type='number' min='0' max='100'> Enter your test score in the range of 0 to 100
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Related

Onclick event in JS shows results for a second before automatically resetting form

This is a simple quiz/questionnaire, that'll display results of the quiz on submission. It shows the results, but only for about half a second before resetting the page. I would also like for the page to show an alert if the user says they're under 18 when the quiz is submitted; it wouldn't keep them from seeing the answers, but just giving them a message.
function checkAge() {
if(age<18) {
alert("Always make sure you have adult supervision while caring for and handling any venomous arachnid.");
} }
function generateAnswers() {
var choice1score = 0;
var choice2score = 0;
var choice3score = 0;
var choice4score = 0;
}
var chosenAnswers = document.getElementsByTagName('result');
for (i=0; i<chosenAnswers.length; i++) {
if (chosenAnswers[i].checked) {
// add 1 to that choice's score
if (chosenAnswers[i].value == 'choice1') {
choice1score = choice1score + 1;
}
if (chosenAnswers[i].value == 'choice2') {
choice2score = choice2score + 1;
}
if (chosenAnswers[i].value == 'choice3') {
choice3score = choice3score + 1;
}
if (chosenAnswers[i].value == 'choice4') {
choice4score = choice4score + 1;
}
}
}
var maxscore = Math.max(choice1score,choice2score,choice3score,choice4score);
var resultBox = document.getElementById('result');
if (choice1score == maxscore) {
resultBox.innerHTML = "Asian Forest Scorpion"
}
if (choice2score == maxscore) {
resultBox.innerHTML = "Deathstalker Scorpion"
}
if (choice3score == maxscore) {
resultBox.innerHTML = "Desert Hairy Scorpion"
}
if (choice4score == maxscore) {
resultBox.innerHTML = "Emperor Scorpion"
}
}
This is where I put the code:
https://codepen.io/cryceks/pen/vjgzOZ
Use:
event.preventDefault();
This prevents the webpage from reloading and therefore clearing form data

This Javascript function keeps looping, how can i make it run once?

The code is meant to animated some text in a typing fashion. I want it to run once, but it keeps looping through the area of sentences. How would i go about stopping it looping through. The top of the code gathers the value of a input and puts into the array, this all works fine. It is just the looping i am having issues with.
var yourWord = document.getElementById("myText").value;
var yourtext = "I took the word " + yourWord + "...";
var infomation = [yourtext,
'I looked at the most relevant news article relating to it...',
'And created this piece of art from the words in the article! '
],
part,
i = 0,
offset = 0,
pollyLen = Polly.length,
forwards = true,
skip_count = 0,
skip_delay = 5,
speed = 100;
var wordflick = function () {
setInterval(function () {
if (forwards) {
if (offset >= infomation[i].length) {
++skip_count;
if (skip_count == skip_delay) {
forwards = false;
skip_count = 0;
}
}
} else {
if (offset == 0) {
forwards = true;
i++;
offset = 0;
if (i >= pollyLen) {
i = 0;
}
}
}
part = infomation[i].substr(0, offset);
if (skip_count == 0) {
if (forwards) {
offset++;
} else {
offset--;
}
}
$('#pollyInfo').text(part);
}, speed);
};
$(document).ready(function () {
wordflick();
});
Modify the line:
setInterval(function () {
into:
var interval = setInterval(function () {
and then clear the interval where you are setting i=0;
if (i >= pollyLen) {
i = 0;
}
to:
if (i >= pollyLen) {
i = 0;
clearInterval(interval);
}
This should do the job!

svgjs how to replay and control series of animations using slider

I am using svg.js and would like to play a series of animations and then provide a slider for the user to control the animation to look at it more closely. It looks like when an animation is complete, svg.js cleans up the resources so the situation gets nulled out so I can't reset it. I have a fiddle with a simplified example. After the animation plays once, I would like to then be able to use the slider and use the at method to reposition the animations.
https://jsfiddle.net/voam/xeajbhea/2/
var draw = SVG('drawing'),
rectA1 = draw.rect(50, 50).fill('#f06'),
rectA2 = draw.rect(50, 50).fill('#60f').translate(200),
rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0),
rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation1 = rectA1.animate().move(200).opacity(0);
var animation2 = rectA2.animate().move(-200).opacity(0).during(function(pos) {
document.getElementById("range_animation").value = pos;
});
var animation3, animation4;
animation2.after(function(situation) {
animation3 = rectB1.animate().move(200).opacity(1);
animation4 = rectB2.animate().opacity(1).move(-200)
.during(function(pos) {
document.getElementById("range_animation").value = 1 + pos;
});
console.log('animations', animation1, animation2, animation3, animation4);
});
document.getElementById("range_animation").addEventListener("change", function(el) {
///console.log('change', el, );
var val = this.value;
if (animation1.situation) {
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
} else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
}
}
}, false);
HTML below:
<div id="drawing">
</div>
<input type="range" id="range_animation" min="0.01" max="2" step=".01" value=".01">
One approach that works is as #Fuzzyma mentioned is to make the animations loop so they don't get terminated. Then one has to manage the transition sets from one to another. Any animations that don't get run immediately start in a paused state. In my real project not this demo there are 5 animation sets each with subanimations so lets see if this turns out to be manageable!
var atEnd = function (pos) {
var rounded = pos.toFixed(2);
return (rounded == .99 || rounded == 1.00);
};
var draw = SVG('drawing')
, rectA1 = draw.rect(50, 50).fill('#f06')
, rectA2= draw.rect(50, 50).fill('#60f').translate(200)
, rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0)
, rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation3, animation4, lastTotalRange;
var animation1 = rectA1.animate().move(200).opacity(0).loop();
var animation2 = rectA2.animate().move(-200).opacity(0).loop();
animation2
.during(function( pos ) {
document.getElementById("range_animation").value = pos;
if ( atEnd (pos) ) {
animation2.pause();
animation3 = rectB1.animate().move(200).opacity(1).loop();
animation4 = rectB2.animate().opacity(1).move(-200).loop();
animation4
.during(function( pos ) {
if (pos > 0) {
document.getElementById("range_animation").value = 1 + pos;
}
if (atEnd (pos)) {
animation4.pause();
}
});
animation3.during( function(pos) {
if (atEnd (pos)) {
animation3.pause();
}
});
}
});
animation1.during(function( pos ) {
if (atEnd (pos)) {
animation1.pause();
}
});
$('input#range_animation').on('input', function() {
$(this).trigger('change');
})
$('input#range_animation').on('change', function() {
var val = $( this).val(),
delta = 0;
if (lastTotalRange) {
delta = val - lastTotalRange;
}
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
if (lastTotalRange > 1) {
animation3.at(.01);
animation4.at(.01);
}
}
else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
if (lastTotalRange < 1) {
animation1.at(.99);
animation1.at(.99);
}
}
// console.log('totalAnimationRange:change', delta, val);
lastTotalRange = val;
})
and the html
<div id="drawing"></div>
<input type="range" id="range_animation" min="0.00" max="1.99" step=".01" value=".01">
and the jsfiddle:
jsfiddle.net/voam/ob2bjfur/1/

Undefined index after running function a few times

So I was trying to create my own Blackjack in javascript for learning purposes and even though the code is overall working, I came across a weird bug.
After some clicks on the Deal html button, which calls the function deal(), I will get either a playerHand[i] undefined or dealerHand[i] undefined error on line 114 or 118, respectively, of the code posted below.
I noticed this also happened if I clicked the button very fast for whatever reason.
I suspected it had something to do with memory optimization so I used the delete command to reset those arrays between game turns, but the error persists.
So, why do my arrays break after some use?
Thanks.
JS:
var deck = [];
var dealerHand = [];
var playerHand = [];
var dscore = 0;
var pscore = 0;
var turn = 0;
function Card(suit, src) {
this.src = src;
this.suit = getSuit(suit);
this.value = getValue(src);
};
function getSuit(suit) {
if (suit == 1) return "Clubs";
if (suit == 2) return "Diamonds";
if (suit == 3) return "Hearts";
if (suit == 4) return "Spades";
};
function getValue(src) {
if (src == 1) return 11;
if (src < 10) return src;
else return 10;
};
function createDeck() {
for (i=1; i<=4; i++) {
for(j=1; j<=13; j++) {
var card = new Card(i, j);
deck.push(card);
};
};
};
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
function deal() {
if(turn == 0) {
dealerHand.push(getCard());
playerHand.push(getCard());
};
dealerHand.push(getCard());
playerHand.push(getCard());
};
function stand() {
dealerHand.push(getCard());
};
function clearBoard () {
$('#player').html("");
$('#dealer').html("");
};
function resetDeck () {
delete deck;
deck = [];
};
function resetHands () {
delete dealerHand;
delete playerHand;
dealerHand = [];
playerHand = [];
};
function resetScore () {
pscore = 0;
dscore = 0;
};
function isAce (arr) {
for(i=0; i<arr.length; i++) {
if (arr[i].src == 1) return true;
else return false;
};
}
function updateScore() {
resetScore();
if (playerHand.length > 0 && dealerHand.length > 0) {
for(i=0; i<playerHand.length; i++) {
pscore += playerHand[i].value;
};
for(i=0; i<dealerHand.length; i++) {
dscore += dealerHand[i].value;
};
//Regra do Às
if(pscore > 21 && isAce(playerHand)) {
pscore -= 10;
};
if(dscore > 21 && isAce(dealerHand)) {
dscore -= 10;
};
} else {
pscore = 0;
dscore = 0;
};
};
function showScore () {
$('#pscore').html("<p>Player Score: " + pscore + "</p>");
$('#dscore').html("<p>Dealer Score: " + dscore + "</p>");
};
function showCards () {
for(i=0; i<playerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + playerHand[i].suit + '/' + playerHand[i].src + '.png');
div.append(img);
$('#player').append(div);
};
for(i=0; i<dealerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + dealerHand[i].suit + '/' + dealerHand[i].src + '.png');
div.append(img);
$('#dealer').append(div);
};
};
function cleanUp () {
if (pscore == 21) {
alert("Blackjack!");
newGame();
};
if (pscore > 21) {
alert("Bust!");
newGame();
};
if (dscore == 21) {
alert("You lost!");
newGame();
};
if (dscore > 21) {
alert("You won!");
newGame();
};
};
function newGame () {
turn = 0;
clearBoard();
resetHands();
resetScore();
showScore();
resetDeck();
createDeck();
};
function gameTurn () {
clearBoard();
updateScore();
showCards();
showScore();
cleanUp();
turn++;
};
$(document).ready(function() {
newGame();
$('#deal').on('click', function(){
deal();
gameTurn();
});
$('#stand').on('click', function(){
stand();
gameTurn();
});
});
CSS:
body {
background: url(../img/greenbg.png);
}
.holder {
width:800px;
margin:auto;
}
.clearfix {
clear:both;
}
#pscore, #dscore {
color: white;
margin: 10px;
display: block;
font-size: 1.2rem;
text-shadow: 0 0 5px #000;
}
.container {
width: 600px;
height: 300px;
margin: 10px;
}
div img {
float: left;
margin: 10px;
}
div button {
margin: 10px;
}
HTML:
<html>
<head>
<div class="holder clearfix">
<div id="dscore"><p>Dealer Score: 0</p>
</div>
<div id="dealer" class="container">
</div>
<div id="pscore"><p>Player Score: 0</p>
</div>
<div id="player" class="container">
</div>
<div class="">
<button id="deal">Deal</button>
<button id="stand">Stand</button>
</div>
</div>
</body>
</html>
You have a problem in this function, which may be to blame:
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
As written, it's removing a card, and then returning the card that now has that position in the deck. If rand was the last element in the array then there is no longer a card in that position, so it'll return undefined.
You should be returning the value of the removed card itself, part of the result of the splice call:
function getCard() {
var rand = Math.floor(Math.random() * deck.length);
var pick = deck.splice(rand, 1);
return pick[0];
};
p.s. it's worth learning modern ES5 utility functions for arrays. For example, your isAce function could be rewritten thus, avoiding the bug where you always return after testing the first element:
function isAce(arr) {
return arr.some(function(n) {
return n === 1;
});
};
or, more cleanly:
function isAce(card) {
return card === 1; // test a single card
};
function holdsAce(hand) {
return hand.some(isAce); // test an array (or hand) of cards
};

Having issues with live calculations, calculating each key stroke

I have a table that calculates a total depending on the input the user types. My problem is that the jquery code is calculating each key stroke and not "grabbing" the entire number once you stop typing. Code is below, any help woud be greatly appreciated.
$(document).ready(function() {
$('input.refreshButton').bind('click', EstimateTotal);
$('input.seatNumber').bind('keypress', EstimateTotal);
$('input.seatNumber').bind('change', EstimateTotal);
});
//$('input[type=submit]').live('click', function() {
function EstimateTotal(event) {
var tierSelected = $(this).attr('data-year');
var numberSeats = Math.floor($('#numberSeats_' + tierSelected).val());
$('.alertbox_error_' + tierSelected).hide();
if (isNaN(numberSeats) || numberSeats == 0) {
$('.alertbox_error_' + tierSelected).show();
} else {
$('.alertbox_error_' + tierSelected).hide();
var seatHigh = 0;
var seatLow = 0;
var seatBase = 0;
var yearTotal = 0;
var totalsArray = [];
var currentYear = 0;
$('.tier_' + tierSelected).each(function() {
seatLow = $(this).attr('data-seat_low');
firstSeatLow = $(this).attr('data-first_seat_low');
seatHigh = $(this).attr('data-seat_high');
seatBase = $(this).attr('data-base_cost');
costPerSeat = $(this).attr('data-cost_per_seat');
years = $(this).attr('data-year');
seats = 0;
if (years != currentYear) {
if (currentYear > 0) {
totalsArray[currentYear] = yearTotal;
}
currentYear = years;
yearTotal = 0;
}
if (numberSeats >= seatHigh) {
seats = Math.floor(seatHigh - seatLow + 1);
} else if (numberSeats >= seatLow) {
seats = Math.floor(numberSeats - seatLow + 1);
}
if (seats < 0) {
seats = 0;
}
yearTotal += Math.floor(costPerSeat) * Math.floor(seats) * Math.floor(years) + Math.floor(seatBase);
});
totalsArray[currentYear] = yearTotal;
totalsArray.forEach(function(item, key) {
if (item > 1000000) {
$('.totalCost_' + tierSelected + '[data-year="' + key + '"]').append('Contact Us');
} else {
$('.totalCost_' + tierSelected + '[data-year="' + key + '"]').append('$' + item);
}
});
}
}
You'll need a setTimeout, and a way to kill/reset it on the keypress.
I'd personally do something like this:
var calc_delay;
$(document).ready(function() {
$('input.refreshButton').bind('click', runEstimateTotal);
$('input.seatNumber').bind('keypress', runEstimateTotal);
$('input.seatNumber').bind('change', runEstimateTotal);
});
function runEstimateTotal(){
clearTimeout(calc_delay);
calc_delay = setTimeout(function(){ EstimateTotal(); }, 100);
}
function EstimateTotal() {
....
What this does is prompt the system to calculate 100ms after every keypress - unless another event is detected (i.e. runEstimateTotal is called), in which case the delay countdown resets.

Categories