I am trying to make a quiz app and i want the score to update. The change event for radio button in not triggered on clicking next question.
https://codepen.io/abhilashn/pen/BRepQz
// JavaScript Document
var quiz = { "JS" : [
{
"id" : 1,
"question" : "Inside which HTML element do we put the JavaScript?",
"options" : [
{"a": "<script>",
"b":"<javascript>",
"c":"<scripting>",
"d":"<js>"}
],
"answer":"<script>",
},
{
"id" : 2,
"question" : "What is the correct JavaScript syntax to change the content of the HTML element below.",
"options" : [
{"a": " document.getElementById('demo').innerHTML = 'Hello World!';",
"b":" document.getElementByName('p').innerHTML = 'Hello World!';",
"c":" document.getElement('p').innerHTML = 'Hello World!';",
"d":" #demo.innerHTML = 'Hello World!';"}
],
"answer":" document.getElementById('demo').innerHTML = 'Hello World!';",
}
]
}
var quizApp = function() {
this.score = 0;
this.qno = 1;
this.currentque = 0;
var totalque = quiz.JS.length;
this.displayQuiz = function(cque) {
this.currentque = cque;
if(this.currentque < totalque) {
$("#qid").html(this.qno++);
$("#question").html(quiz.JS[this.currentque].question);
$("#question-options").html("");
for (var key in quiz.JS[this.currentque].options[0]) {
if (quiz.JS[this.currentque].options[0].hasOwnProperty(key)) {
//console.log(key + " -> " + quiz.JS[this.currentque].options[0][key]);
$("#question-options").append(
"<div class='form-check option-block'>" +
"<label class='form-check-label'>" +
"<input type='radio' class='form-check-input' name='option' id='q"+key+"' value='" + quiz.JS[this.currentque].options[0][key] + "'>" +
quiz.JS[this.currentque].options[0][key] +
"</label>"
);
}
}
} else {
return alert("Your score: " + this.score) ;
}
}
this.checkAnswer = function(option) {
var answer = quiz.JS[this.currentque].answer;
option = option.replace(/\</g,"<") //for <
option = option.replace(/\>/g,">") //for >
console.log(answer);
console.log(option);
if(option == quiz.JS[this.currentque].answer) {
this.score = this.score + 1;
console.log(this.score);
}
}
this.changeQuestion = function(cque) {
this.currentque = this.currentque + cque;
this.displayQuiz(this.currentque);
}
}
var jsq = new quizApp();
$(document).ready(function() {
jsq.displayQuiz(0);
$('input[type=radio][name=option]').change(function(e) {
e.preventDefault();
if (this.checked) {
jsq.checkAnswer(this.value);
}
});
});
$('#next').click(function(e) {
e.preventDefault();
jsq.changeQuestion(1);
});
You are only applying the event handler on the elements that are already present, and not on the one that will be created later.
The event handler should be on an element that is a parent to all the future elements.
Like this:
$('#question-options').on('change', 'input[type=radio][name=option]', function(e) {
// code
});
From jQuery documentation on on:
Event handlers are bound only to the currently selected elements; they must exist at the time your code makes the call to .on(). To ensure the elements are present and can be selected, [...] use delegated events to attach event handlers.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
Related
When the button is clicked, 2 sets data is added. I use material design.
Button needs 2 clicks to run function for first time. Due to this, the data is added to table 2 times.
Code
HTML
<button onclick="purchaseList(orderid)" id="dialog">Button</button>
JS
function popup(listid) {
var starCountRef = firebase.database().ref('Orders/' +
listid).child('foodItems');
starCountRef.on('child_added', snapshot => {
var snaps = snapshot.val();
var itemPrice = snaps.price;
var itemName = snaps.productName;
var itemQuantity = snaps.quantity;
console.log(itemName);
$("#producttable").append(
'<tr><td class="mdl-data-table__cell--non-numeric">' + itemName +
'</td><td>' + itemQuantity + '</td><td>' + itemPrice + '</td></tr>'
);
});
var dialog = document.querySelector('dialog');
var showDialogButton = document.querySelector('#dialog');
if (!dialog.showModal) {
dialogPolyfill.registerDialog(dialog);
}
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
var element = document.getElementById("producttable")
while (element.lastChild) {
element.removeChild(element.lastChild);
}
dialog.close();
});
}
This should work:
var element = document.getElementById("producttable")
while (element.lastChild) {
element.removeChild(element.lastChild);
}
Add this as necessary.
I suggest you change your firebase function from using .on to .once to avoid multiple additions of data to your table and as your data isn't expected to change frequently or require active listening you better use .once for performance benefits.
firebase.database().ref('Orders/' +
listid + '/foodItems').once('value').then(function(snapshot) {
// the rest of your code goes here
});
this remocve element with class name ".mdl-data-table__cell--non-numeric"
when user click .close
dialog.querySelector('.close').addEventListener('click', function () {
dialog.close();
$(".mdl-data-table__cell--non-numeric").remove();
});
UPDATE:
to open dialog on 2nd click use pseudo element to activate like this
<div class=pseudo><button onclick="purchaseList(orderid)"id="dialog" disabled>Button</button></div>
var i=0;
$('.pseudo').click(function(){
i++;
if(i==2){
$("#dialog").prop('disabled',false);
}
});
I am trying to append a number of Div's to a div with an id "list", and each div has an event so i make an array for each div to be appended.
here is my code.
var count = Object.keys(data.results).length;
var el = [];
for(var i=1; i<=count; i++){
el[i] = $('<div id="'+i+'">data.results[i].name</div>');
$("#list").append(el[i]);
el[i].click(function(){
alert(data.results[i].name);
$('#searchbox').modal('toggle');
});
}
the data in div's was successfully appended. but as a try to alert the data in the event i bind to each div, it doesn't alert the data in the div.
what I am trying to do is append names with a div within the div with id "list" and if i click on a name, it should alert the name itself.
You can simplify the logic here by using a delegated event handler on all the appended div elements, then using the text() method to retrieve the required value. Try this:
var data = {
results: {
foo: { name: 'foo_name' },
bar: { name: 'bar_name' }
}
}
var $list = $("#list").on('click', 'div', function() {
console.log($(this).text());
//$('#searchbox').modal('toggle');
});
Object.keys(data.results).forEach(function(key, i) {
$list.append('<div id="' + i + '">' + data.results[key].name + '</div>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="list"></div>
The problem is that by the time an element is clicked i is already set to the maximum value i = count. To fix that you'll have to create a closure. Try this:
var count = Object.keys(data.results).length;
var el = [];
function closure(index){
el[index].click(function(){
alert(data.results[index].name);
$('#searchbox').modal('toggle');
});
}
for(var i=1; i<=count; i++){
el[i] = $('<div id="'+i+'">data.results[i].name</div>');
$("#list").append(el[i]);
closure(i);
}
Here is how I populate my list:
function bingNetworksList(myList) {
var list = '';
for (i = 0; i < myList.length; i++) {
list += '<li>' + myList[i] + '</li>';
}
$('#myList').empty();
$('#myList').append(list);
}
Here is my html file:
<ul id="myList"></ul>
I want to add a click event for every item of list (without having separate ids):
$(function() {
$('#myList').click(function() {
var listItem = this.find('a').text();
console.log(listItem); // never logged
});
});
However, when I click at a list item, click event doesn't fire.
What am I doing wrong?
I can only assume there's a js error in your console.
I've created a working sample for you. We can use event delegation and then retrieve the DOM node that was clicked. You need to ensure you call the bingNetworksList [assume typo in here and meant binD ;)] function when the DOM ready event has fired.
function bingNetworksList(myList) {
var list = '';
for (i = 0; i < myList.length; i++) {
list += '<li>' + myList[i] + '</li>';
}
$('#myList').empty();
$('#myList').append(list);
}
$(function() {
var list = ["foo", "bar"]
bingNetworksList(list);
$('#myList').click(function(evt) {
var listItem = $(evt.target).text();
console.log(listItem); // never logged
});
});
You need to wrap this inside $ as like this:
$(function() {
$('#myList').click(function() {
var listItem = $(this).find('a').text();
console.log(listItem); // will always be logged
});
});
I have a loop that creates Html links and assign click events to them in the same loop. The click event seems to work only for the last element that the loop creates.
<div id="list">
</div>
<script type="text/javascript">
users = {
1 : { id : 1 },
2 : { id : 2 },
3 : { id : 3 },
4 : { id : 4 }
};
for (var index in users) {
var user = users[index];
document.getElementById("list").innerHTML += '' + user.id + '';
var element = document.querySelector("#user-" + user.id);
console.log(element);
element.addEventListener("click", function(event){
console.log(this);
event.preventDefault();
});
}
</script>
Here only the last link is click-able. However if the links have already been created then all the event listeners will work.
<div id="list">
1
2
3
4
</div>
<script type="text/javascript">
users = {
1 : { id : 1 },
2 : { id : 2 },
3 : { id : 3 },
4 : { id : 4 }
};
for (var index in users) {
var user = users[index];
var element = document.querySelector("#user-" + user.id);
console.log(element);
element.addEventListener("click", function(event){
console.log(this);
event.preventDefault();
});
}
</script>
This seems to work without any issues. Is there away to make sure that anevent is attached to the element at creation?
Note: I don't want to use JS framework for this.
I know that this can be done easily using JQuery.
$( "body" ).on('click', "#user-" + user.id ,function(){});
Note:
The code I posted is a simple code I created on the fly to illustrate the problem. I am using a template engine that return a string, that's why I am using
document.getElementById("list").innerHTML += '' + user.id + '';
Your problem is in .innerHTML which breaks the DOM with every iteration, which means the previously added events are wiped out and you only have the listener attached to the very last element. You can use DOM API to create and append these elements and everything will work fine: jsfiddle.net/HtxZb
I would, however, recommend using event delegation. Without jQuery you can implement it like this:
document.getElementById('list')
.addEventListener('click', function(event){
var elem = event.target;
console.log( elem );
if( elem.nodeName.toLowerCase() === "a" ){ // a link was clicked
/* do something with link
for example
switch( elem.id ){
}
*/
event.preventDefault();
}
});
Its better to attach the onclick handler before appending to the DOM element.
This should work
<script type="text/javascript">
users = {
1 : { id : 1 },
2 : { id : 2 },
3 : { id : 3 },
4 : { id : 4 }
};
for (var index in users) {
var user = users[index];
var a = document.createElement("a");
a.href='#';
a.id = "user-" + user.id ;
a.onclick = function(event){
console.log(this);
event.preventDefault();
}
a.innerHTML = user.id;
document.getElementById("list").appendChild(a);
}
</script>
Try the following at this fiddle:
<div id="list">
</div>
users = { 1 : { id : 1 },
2 : { id : 2 },
3 : { id : 3 },
4 : { id : 4 } };
for (var index in users) {
var user = users[index];
var element = document.createElement("a");
element.id = "user-" + user.id;
element.innerHTML = "user-" + user.id;
document.getElementById("list").appendChild(element);
console.log(element);
element.onclick = function(event){
console.log(this);
event.preventDefault();
};
}
I am populating a table with an XML file, I have a column that links to more details. Because of the way I'm running the web page (Chrome extension) I need to dynamically add an event handler when the table is populated.
I have this working...
document.addEventListener('DOMContentLoaded', function () {
document.getElementById("detailLink").addEventListener('click',
clickHandlerDetailLink); });
function clickHandlerDetailLink(e) { detailLinkPress('SHOW'); }
function detailLinkPress(str) {
alert("Message that will show more detail");
}
But how do I go about adding the event handler dynamically? I have assigned all the fields in that column the id of detailLink.
You probably need to listen for a mutation event for the table, and then check each time the target element which has fired the event. Previously it used to be these events "DOMNodeInserted", or "DOMSubtreeModified", but they were very slow so according to new specifications the listener is called MutationObserver (which is much faster than the previous ones). This is an example from some Mozilla webpage edited for my testing :
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
alert(mutation.target.id + ", " + mutation.type +
(mutation.addedNodes ? ", added nodes(" + mutation.addedNodes.length + "): " + printNodeList(mutation.addedNodes) : "") +
(mutation.removedNodes ? ", removed nodes(" + mutation.removedNodes.length + "): " + printNodeList(mutation.removedNodes) : ""));
});
});
// configuration of the observer:
var config = { attributes: false, childList: true, characterData: false };
var element = document.getElementById('TestID');
// pass in the target node, as well as the observer options
observer.observe(element, config);
function printNodeList(nodelist)
{
if(!nodelist)
return "";
var i = 0;
var str = "";
for(; i < nodelist.length; ++i)
str += nodelist[i].textContent + ",";
return str;
}
If you want to assign an event to an element that doesn't yet exist, or to a series of elements (without creating one for each element), you need a delegate. A delegate is simply a parent element that will listen for the event instead of all the children. When it handles the event, you check to see if the element that threw the event is the one you're looking for.
If the parent <table> always exits, that would be a good place to add the listener. You can also add it to body. Also, you shouldn't be using detailLink as an id for more than one element. Use class instead.
Demo:
Script:
document.body.addEventListener( 'click', function ( event ) {
if( event.srcElement.className == 'detailLink' ) {
detailLinkPress( 'SHOW' );
};
} );
function detailLinkPress( str ) {
alert("Message that will show more detail");
};
HTML:
<div class="detailLink">click me</div>