Javascript - Display quiz questions based on URL - javascript

I have a simple javascript quiz set up on codepen http://codepen.io/anon/pen/dYNKJL
It displays one question per page and once you select an answer, you move onto the next question. Whatever answer is selected is given the class "active" so I can get all the selected answers at the end of the quiz.
Each answer has a data-quizIndex of either 1 or 2. When I collect all the "active" answers, I can see if they were answered data-quizIndex of 1 (first answer) or vice versa.
Anyways, up until this point, things are working great. However, I am now attempting to do something else. The user is directed to this quiz via a link, and the link will be one of the following
www.something.com/index.html?qa=0
www.something.com/index.html?qa=1
www.something.com/index.html?qa=2
The page they are coming from has a preview of question one which they can answer. Using javascript, I need to get the qa value which shouldnt be a problem. If qa is 0, the quiz starts from question 1 and nothing needs to be done. If qa is 1 or 2, the quiz should start from question two and the active class for question 1 should be set to li with data-quizIndex 1 or data-quizIndex 2 (depending on the ga value).
In other words, if they answer question one from the page that redirects them to the quiz, I need to make it so question one is answered within the main quiz application.
Would this be possible and if so, what is the best way to achieve this?
Thanks

You can define a queryString property into location, e.g:
if (typeof location.queryString === 'undefined') {
Object.defineProperty(location, 'queryString', {
get: function() {
return location.search.slice(1).split('&').map(function(i) {
var arr = i.split('=');
var a = {};
a[decodeURIComponent(arr[0])] = arr[1] ? decodeURIComponent(arr[1]) : void 0;
return a;
}).reduce(function(a, b) {
var key = Object.keys(b)[0];
a[key] = b[key];
return a;
});
}
});
}
And then, you can use it when the page is loaded, like this:
$(function() {
var qa = location.queryString['qa'];
if (qa === '1' || qa === '2') {
$('.current .quiz-answer')[qa - 1].click();
}
});
Since you want to simulate that the first question was answered, much better than repeating the code is to simulate as the user has clicked it. Take a look at your updated codepen.
Change the querystring, and loads it again, and you'll see that it's working like a charm! :)

Related

Prevent users from skipping my Quiz questions

Right now I have a few issues I need to tweak out with my Quiz Application.
When the user answers the first question, he can skip the proceeding questions.
var usersGuess = -1; //this is the starting value of the userGuess variable
This is because the userGuess variable is updated by the id of the radio button clicked when the user makes a guess, and the default starting variable is what prevents the user from skipping the first question only. I have a few ideas on how to fix it, but not sure how to implement it. One would be to detect IF none of the radio inputs are checked then don't load the next question, else load it.
When a user goes back to the question, the previous checked radio button disappears. I haven't thought of how to fix this yet. Right now I have saved the previous answers into an empty array, but I don't believe it's working properly.
Here is the code that deals with userGuess and how it interacts with the Event Listener for my next button.
nextButton.addEventListener('click', function() {
//prevent user from skipping questions
if (usersGuess === -1) {
console.log(usersGuess);
alert('You must choose an answer!');
} else {
//if users guess is the id of the correct answer, add one
if (usersGuess == questions[index].correctAnswer) {
userScore += 1;
console.log('Users Score: ' + userScore);
}
//store previous user answers into an array
usersAnswers.push(usersGuess);
if (index + 1 < questions.length) {
index++;
addQuizQuestion(questions[index].question);
addQuizAnswers(questions[index].choices);
} else {
alert('You scored a ' + getPercent() + '%!');
}
}
}, false);
Here is a link to the codepen as well for testing: http://codepen.io/laere/pen/KVwBME
Appreciate the help!
Don't complicate things too much. All you need here is the index of the current question and, say, an array of boolean (or integers, for points) for each question.
All you need to do is to check whether the current question has been answered, and if so, allow the user to move on.
Of course, since this is Javascript, all this serves for a better user experience and nothing more - if someone WANTS to move on to the next question, they WILL (unless you implement some server-side checking/question loading).
reset usersGuess to -1 before loading next question.
if (index + 1 < questions.length) {
index++;
usersGuess = -1; // reset usersGuess
addQuizQuestion(questions[index].question);
addQuizAnswers(questions[index].choices);
}

cannot read shorthand javascript/query

i've spent ages trying to understand bootstrap's navigation bar, mainly by spending 4-5 days reading stackoverflow posts
& finally i think i've found an answer that helps!!!
trouble is, i can't understand the accompanying javascript/jquery code. i'm guessing its a shorthand version of js or something but just what it means i cannot decipher
basically, its the javascript code that appears on this jsfiddle page
$('.navbar').on('show', function () {
var actives = $(this).find('.collapse.in'),
hasData;
if (actives && actives.length) {
hasData = actives.data('collapse')
if (hasData && hasData.transitioning) return
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
});
so, if anyone can explain to me what the code is doing on a line by line basis it'd be really cool
the first line i understand. its the weird-ass syntax in the next 6 lines that have me mystified
var actives = $(this).find('.collapse.in'),
hasData;
This creates two variables. One with elements picked from current scope that match the selector .collapse.in, and one empty variable.
if (actives && actives.length)
If actives exists and contains more than zero elements, do the following...
hasData = actives.data('collapse')
Retrieve arbitrary data stored under the key collapse. See https://api.jquery.com/jquery.data/ for more info.
if (hasData && hasData.transitioning) return
If hasData exists and hasData.transitioning is truthy, stop function execution.
actives.collapse('hide')
Call the collapse function on actives. This is not a native jQuery function, so you'll have to look up whatever plugin it comes from to make sense of the argument being passed in.
hasData || actives.data('collapse', null)
If hasData is truthy, skip this line. Otherwise, set the arbitrary data in actives variable to null.

How to create an html context menu where the menu entries changed based on runtime values?

I'm unable to answer the question as it's been put on hold. But I've added an update below.
Ok, this is probably a really dumb question - but my head hurts too much now!
I am trying to create a context menu that changes based on certain conditions. So, purely for example if the hour is less than 8 then show a menu with entries:
call today,
visit today
email
If the hour is between 8 & 4 the entries might be:
call now
email now
If the hour is between 4 & 12 then another set of menu entries.
I've tried some examples I've found online but I just seem to be getting more confused now!
Ok, I said it was a dumb question. I had looked at various plugins, such as Audero-Context-Menu # github.com/AurelioDeRosa/Audero-Context-Menu, MB Menu # pupunzi.open-lab.com/mb-jquery-components/mb-_menu/ and others. I've ended up with Rodney Rehm's jQuery-contextMenu. After looking through it and some other sources, I was able to take one of the demos and modify it to prototype what I was trying to accomplish. Based on the demo # hmedialize.github.io/jQuery-contextMenu/demo/dynamic-create.html I modified the code as shown below:
$(function(){
$.contextMenu({
selector: '.context-menu-one',
build: function($trigger, e) {
// this callback is executed every time the menu is to be shown
// its results are destroyed every time the menu is hidden
// e is the original contextmenu event, containing e.pageX and e.pageY (amongst other data)
return {
callback: function(key, options) {
process(key, options)
},
items: loadme()
};
}
});
});
var count=0;
function process(key, options) {
var m = "clicked: " + key;
window.console && console.log(m) || alert(m);
}
function loadme() {
return {
"edit": {name: "Edit "+ (++count)},
"sep1": "---------",
"quit": {name: "Quit", icon: "quit"}
}
};
Basically, each time you bring up the context menu, the count increments for the Edit link in the menu.
P.S. I would use the proper links but I can only use two due to my reputation!
Notice that comment for the build param:
// this callback is executed every time the menu is to be shown
And you return an object containing the loadme function, which increments count each time it's called (that's what ++count does). The result is that every time a menu shows, loadme runs and count gets incremented. You'd want to get rid of the ++ if you don't want the increment to happen.
But i'm thinking this is just an example, and isn't related to what you're really trying to do. What you almost certainly want is to make loadme instead decide which range (new Date()).getHours() falls into, and based on that, return an object containing the appropriate menu entries.

Honestly hard to explain.. I use something like a=b, then a++, but b changes. Except, these are Arrays

Well my short and easy to explain explanation can be this. I have 2 arrays, FilterList and GamesReset. Whenever this function I have works and filters out some games with check boxes and a drop down menu, the function starts off with something like FilterList=GamesReset;. This functions seems to work fine until I filter out ages for the game. The function never touches GamesReset unless it's something like while(i<GamesReset.length){} or FilterList=GamesReset;. And the only tool I use when I filter games is FilterList.splice(i,1);. Now with that, GamesReset definitely, should never change as far as I know. I have it to reset FilterList, then depending on what needs to be filtered out, it will start removing those games from the FilterList. The problem I have, is that, GamesReset also becomes filtered. Which, does not make any sense at all. So like my title, it's just like saying b=0;, a=b;, a++;, and now b equals 1.
Now, I think that's the best/shortest way I can reveal this problem, without overdoing it with my bad habit of explaining things to people. I have a webpage currently available if anyone would like to see whats going on in action, because I wouldn't get what's going on with GamesReset either if I were you, here (url removed, read edit). To get the error working, just change the age to 10 without checking any boxes. The bottom paragraph is the GamesReset array (using <br> to separate each array), and it's the one that changes when I'm only changing FilterList in the JavaScript. The actual codes if you view the page source may be a little off compared to when I mentioned above, but it's pretty much 100% the same thing. I also wanted to have the codes available without a url and on this page, but I can't figure out how to do that with the html tags included.
Actually, here's the JavaScript function. I just figured out the 4 spaces thing when my question was rejected.
function SearchFilter() {
Games = GamesReset;
plat = document.getElementById('platformcheck').checked;
rpg = document.getElementById('rpgcheck').checked;
puzz = document.getElementById('puzzlecheck').checked;
hybo = document.getElementById('hybocollectcheck').checked;
ages = document.getElementById('agescheck').value;
if ((!plat) && (!rpg) && (!puzz) && (!hybo)) {
FilterList = Games;
} else {
FilterList = [];
i = 0;
while (i < Games.length) {
Set = '';
Set = Games[i];
Set = Set.split('</>');
StrFind = Set[0];
if (
(plat && (StrFind.search(',platform,') > -1)) || (rpg && (StrFind.search(',rpg,') > -1)) || (puzz && (StrFind.search(',puzzle,') > -1)) || (hybo && (StrFind.search(',hybocollect,') > -1))) {
FilterList.push(Games[i]);
}
i++;
}
// so by now, we should have the filtered array
}
//seperate filter for ages
i = 0;
while (i < FilterList.length) { //The problem should definitely start here
Set = '';
Set = FilterList[i];
Set = Set.split('</>');
StrFind = Set[1];
if ((Math.abs(StrFind)) > ages) {
FilterList.splice(i, 1);
} else {
i++;
}
}
GL.innerHTML = GamesReset.join('<br>');
}
As a reminder, the problem starts when the age filter is working. And the only thing it does is FilterList.splice(i,1);. But it ends up changing GamesReset. I changed this function a bit when I added Games=GamesReset;, but that was another test to try and make sure GamesReset doesn't get filtered like FilterList, but it still does.
EDIT: I removed my url since the answers definitely explained everything, so there's no need for it now.
Arrays are not copied when assigned, both variables will refer to the same data. Here is a post that goes into detail on this: Copying array by value in JavaScript
It makes perfect sense since variables are just references to objects in memory. One object can have several references. Consider this:
var a = { foo: 'bar' };
var b = a;
// b is now a reference to a and they both point to the same object
b.foo = 'doe';
alert( a.foo ); // alerts doe
The same goes for arrays. So when you do FilterList = GamesReset you are not copying the array - you are just assigning the same array to another variable. Any mutations or changes made to either reference will be reflected in all references.
To create a copy of an array you can use slice:
FilterList = GamesReset.slice();

How do I adapt this quiz-answering script to this new quiz layout?

I want the browser to be able to select correct answers to questions.
I found an answer that seems to be the right idea (How can I automatically select specific radio buttons with Greasemonkey?) but I don't know enough javascript to use it.
My HTML code is shown in this fiddle.
Desired output example should look something like this:
(Click for larger image)
How do I adapt that solution to my target page?
In this case, the art is in: (1) identifying the best jQuery selectors for the question and the answers and (2) tuning the ansForThisQ.each() loop. For your HTML:
var questionTxt = $("span.sorumetin");
and
var ansForThisQ = $(this).next ("ul.sorular").find ("a");
ansForThisQ.each ( function () {
var zRegExp = new RegExp (answerTxt, 'i');
if (zRegExp.test (this.textContent) ) {
bFoundAnswer = true;
$(this).css ("background", "lime");
return false; // End loop
}
} );
See the updated Fiddle.
Reference and learn jQuery selectors and jQuery traversing.
I dont have a ton of time to review your code, but you would probably be easiest to store a variable of 1, 2, 3, or 4, based on the position of the correct answer, then when clicked, simply have 4 nested if statements IE - if 1, highlight 1, if 2, etc.

Categories