String to node error - javascript

I've encountered an error - "Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'" on line 113. As far as I understand (I'm a beginner), the problem is that the variable "shoppingListItem" isn't a Node, but a String. How can I fix this?
var shoppingList = {
list: [{
item: 'milk',
isBought: false,
itemCounter: 1
}, {
item: 'beer',
isBought: false,
itemCounter: 1
}, {
item: 'sugar',
isBought: false,
itemCounter: 1
}],
displayShoppingList: function() {
//debugger;
if (this.list.length > 0) {
for (var x = 0; x < this.list.length; x++) {
if (this.list[x].isBought === true) {
console.log('(x)' + this.list[x].item);
} else {
console.log('( )' + this.list[x].item);
}
}
} else {
console.log('Your shopping list is empty');
}
},
addToShoppingList: function(item) {
this.list.push({
item: item,
isBought: false,
itemCounter: 1
});
this.displayShoppingList();
},
changeShoppingList: function(place, newItem) {
this.list[place].item = newItem;
this.displayShoppingList();
},
changeCounter: function(place, newCounter) {
this.list[place].itemCounter = newCounter;
this.displayShoppingList();
},
makeItemBought: function(place) {
this.list[place].isBought = !this.list[place].isBought;
this.displayShoppingList();
},
deleteFromShoppingList: function(place) {
this.list.splice(place, 1);
this.displayShoppingList();
},
toggleAllItems: function() {
var allItems = this.list.length;
var boughtItems = 0;
for (var y = 0; y < allItems; y++) {
if (this.list[y].isBought === true) {
boughtItems++;
}
}
if (boughtItems === allItems) {
for (var z = 0; z < allItems; z++) {
this.list[z].isBought = false;
}
} else {
for (var a = 0; a < this.list.length; a++) {
this.list[a].isBought = true;
}
}
this.displayShoppingList();
}
};
var handlers = {
showAll: function() {
shoppingList.displayShoppingList();
},
toggleAll: function() {
shoppingList.toggleAllItems();
showOnScreen.displayShoppingList();
},
addToShoppingList: function() {
var addToShoppingListInput = document.getElementById('addToShoppingListText');
shoppingList.addToShoppingList(addToShoppingListInput.value);
addToShoppingListInput.value = "";
showOnScreen.displayShoppingList();
},
changeShoppingList: function() {
var changeShoppingListInputNumber = document.getElementById('changeShoppingListNumber');
var changeShoppingListInputText = document.getElementById('changeShoppingListText');
shoppingList.changeShoppingList(changeShoppingListInputNumber.valueAsNumber, changeShoppingListInputText.value);
changeShoppingListInputNumber.value = "";
changeShoppingListInputText.value = "";
showOnScreen.displayShoppingList();
},
};
var showOnScreen = {
displayShoppingList: function() {
var shoppingUnorderedList = document.querySelector('ul');
shoppingUnorderedList.innerHTML = '';
for (var x = 0; x < shoppingList.list.length; x++) {
var shoppingListItem = document.createElement('li');
var isBoughtDisplay = '';
if (shoppingList.list[x].isBought === true) {
isBoughtDisplay = '(x)' + shoppingList.list[x].item + ' ' + shoppingList.list[x].itemCounter;
} else {
isBoughtDisplay = '( )' + shoppingList.list[x].item + ' ' + shoppingList.list[x].itemCounter;
}
shoppingListItem.textContent = isBoughtDisplay;
shoppingListItem.appendChild(this.createDeleteButton); // error is here
shoppingUnorderedList.appendChild(shoppingListItem);
}
},
createDeleteButton: function() {
var deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete item';
deleteButton.className = 'deleteButtonClass';
return deleteButton;
}
};
//shoppingList.addToShoppingList('muffin');
//shoppingList.toggleAllItems();
//add multiple items at the same time - divide them by “,” and push one by one(?)
//counter on each item - add ‘counter’ property to item/isBought, increase by one (tap) or manually by counter (how? - figure out!)
//swipe movements and mobile devices adaptation (read docs)
<!DOCTYPE html>
<html>
<body>
<h1>Список покупок</h1>
<button onclick='showOnScreen.displayShoppingList()'>Show Shopping List</button>
<button onclick='handlers.toggleAll()'>Toggle all on/off</button>
<div>
<input type='text' id='addToShoppingListText'>
<button onclick='handlers.addToShoppingList()'>Add to shopping list</button>
</div>
<div>
<input type='number' id='changeShoppingListNumber'>
<input type='text' id='changeShoppingListText'>
<button onclick='handlers.changeShoppingList()'>Change Shopping List Item</button>
</div>
<div>
<input type='number' id='changeCounterPlace'>
<input type='number' id='changeCounterValue'>
<button onclick='handlers.changeCounter()'>Change number of items</button>
</div>
<ul>
</ul>
<script src="script.js"></script>
</body>
</html>

The "createDeleteButton" is a function but you are not call it. Just change to :
shoppingListItem.appendChild(this.createDeleteButton());
And it should work

Related

calculate score based on array object using jquery

I’m beginner in JavaScript and I’m trying to make a quiz that calculates the score of object(type) in the array. So the total score should count how much the user get in each part(ex:2/2 in math, 1/2 in science. Total= 3/4).
Is there any possible way to do that using jquery ?
help me please, thank you
var all_questions = [{
type:"math",
question_string: "4 + 4",
choices: {
correct: "8",
wrong: ["2", "3", "9"]
}
}, {
type:"math",
question_string: "4 * 4",
choices: {
correct: "16",
wrong: ["24", "13", "4"]
}
}, {
type:"sience",
question_string: "What part of the body helps you move?",
choices: {
correct: "Muscles",
wrong: ["Eyes", "Pancreas", "Lungs"]
}
}, {
type:"sience",
question_string: 'What star shines in the day and provides light?',
choices: {
correct: "Sun",
wrong: ["Moon", "Venus", "Mars"]
}
}];
var Quiz = function(quiz_name) {
this.quiz_name = quiz_name;
this.questions = [];
}
Quiz.prototype.add_question = function(question) {
var index_to_add_question = Math.floor(Math.random() * this.questions.length);
this.questions.splice(index_to_add_question, 0, question);
}
Quiz.prototype.render = function(container) {
var self = this;
$('#quiz-results').hide();
$('#quiz-name').text(this.quiz_name);
var question_container = $('<div>').attr('id', 'question').insertAfter('#quiz-name');
function change_question() {
self.questions[current_question_index].render(question_container);
$('#prev-question-button').prop('disabled', current_question_index === 0);
$('#next-question-button').prop('disabled', current_question_index === self.questions.length - 1);
var all_questions_answered = true;
for (var i = 0; i < self.questions.length; i++) {
if (self.questions[i].user_choice_index === null) {
all_questions_answered = false;
break;
}
}
$('#submit-button').prop('disabled', !all_questions_answered);
}
var current_question_index = 0;
change_question();
$('#prev-question-button').click(function() {
if (current_question_index > 0) {
current_question_index--;
change_question();
}
});
$('#next-question-button').click(function() {
if (current_question_index < self.questions.length - 1) {
current_question_index++;
change_question();
}
});
$('#submit-button').click(function() {
var score = 0;
for (var i = 0; i < self.questions.length; i++) {
if (self.questions[i].user_choice_index === self.questions[i].correct_choice_index) {
score++;
}
}
var percentage = score / self.questions.length;
console.log(percentage);
var message;
if (percentage === 1) {
message = 'Great job!'
} else if (percentage >= .75) {
message = 'You did alright.'
} else if (percentage >= .5) {
message = 'Better luck next time.'
} else {
message = 'Maybe you should try a little harder.'
}
$('#quiz-results-message').text(message);
$('#quiz-results-score').html('You got <b>' + score + '/' + self.questions.length + '</b> questions correct.');
$('#quiz-results').slideDown();
$('#quiz button').slideUp();
});
question_container.bind('user-select-change', function() {
var all_questions_answered = true;
for (var i = 0; i < self.questions.length; i++) {
if (self.questions[i].user_choice_index === null) {
all_questions_answered = false;
break;
}
}
$('#submit-button').prop('disabled', !all_questions_answered);
});
}
var Question = function(question_string, correct_choice, wrong_choices) {
// Private fields for an instance of a Question object.
this.question_string = question_string;
this.choices = [];
this.user_choice_index = null; // Index of the user's choice selection
// Random assign the correct choice an index
this.correct_choice_index = Math.floor(Math.random() * wrong_choices.length + 1);
// Fill in this.choices with the choices
var number_of_choices = wrong_choices.length + 1;
for (var i = 0; i < number_of_choices; i++) {
if (i === this.correct_choice_index) {
this.choices[i] = correct_choice;
} else {
// Randomly pick a wrong choice to put in this index
var wrong_choice_index = Math.floor(Math.random(0, wrong_choices.length));
this.choices[i] = wrong_choices[wrong_choice_index];
// Remove the wrong choice from the wrong choice array so that we don't pick it again
wrong_choices.splice(wrong_choice_index, 1);
}
}
}
Question.prototype.render = function(container) {
var self = this;
var question_string_h2;
if (container.children('h2').length === 0) {
question_string_h2 = $('<h2>').appendTo(container);
} else {
question_string_h2 = container.children('h2').first();
}
question_string_h2.text(this.question_string);
if (container.children('input[type=radio]').length > 0) {
container.children('input[type=radio]').each(function() {
var radio_button_id = $(this).attr('id');
$(this).remove();
container.children('label[for=' + radio_button_id + ']').remove();
});
}
for (var i = 0; i < this.choices.length; i++) {
var choice_radio_button = $('<input>')
.attr('id', 'choices-' + i)
.attr('type', 'radio')
.attr('name', 'choices')
.attr('value', 'choices-' + i)
.attr('checked', i === this.user_choice_index)
.appendTo(container);
var choice_label = $('<label>')
.text(this.choices[i])
.attr('for', 'choices-' + i)
.appendTo(container);
}
$('input[name=choices]').change(function(index) {
var selected_radio_button_value = $('input[name=choices]:checked').val();
self.user_choice_index = parseInt(selected_radio_button_value.substr(selected_radio_button_value.length - 1, 1));
container.trigger('user-select-change');
});
}
$(document).ready(function() {
var quiz = new Quiz('');
for (var i = 0; i < all_questions.length; i++) {
var question = new Question(all_questions[i].question_string, all_questions[i].choices.correct, all_questions[i].choices.wrong);
quiz.add_question(question);
}
var quiz_container = $('#quiz');
quiz.render(quiz_container);
});
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Multiple Choice Quiz</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div id="quiz">
<h1 id="quiz-name"></h1>
<button id="submit-button">Submit</button>
<button id="next-question-button">Next</button>
<button id="prev-question-button">Back</button>
<div id="quiz-results">
<p id="quiz-results-message"></p>
<p id="quiz-results-score"></p>
</div>
</div>
<!-- partial -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script><script src="./script.js"></script>
</body>
</html>
Basically you're looking for a group by logic - you want to group the answers by the question type. In order to solve this:
Add question_type to Question (not mandatory, we can take it from the original array of question. I'll keep it for readability).
In order to implement the group by logic, we can reduce to filter the right answers and convert the Questions array into a score dictionary. The keys are the questions type, the value is the score.
For every right answer, we either adding plus one if the category already exists or initiating as 1 if not.
I'm using a shorter version yet does exactly the same:
acc.set(curr.question_type, (acc.get(curr.question_type) || 0) + 1);
// |-- add / update --| |- current score if exists -| 0 if not, plus one
Here is the full reduce
// global counter for total answers
let totalScore = 0;
const result = a.reduce((acc, curr) => {
// if it's the right answer
if (curr.user_choice_index === curr.correct_choice_index) {
// add / update the entity with 1 if's the first right answer in the category so far or add 1 if it exists
acc.set(curr.question_type, (acc.get(curr.question_type) || 0) + 1);
totalScore++;
}
return acc;
}, new Map());
The second part is to render the results:
// convert the Map back to array
const scoreText = Array.from(score.entries())
// render each type in a different line
.map(([type, scoreByType]) => `<b>${type}</b>: ${scoreByType}`)
.join('<br />');
Read more about
Map
Map.entries
Array.from
Array.reduce
Full working example:
var all_questions = [{
type:"math",
question_string: "4 + 4",
choices: {
correct: "8",
wrong: ["2", "3", "9"]
}
}, {
type:"math",
question_string: "4 * 4",
choices: {
correct: "16",
wrong: ["24", "13", "4"]
}
}, {
type:"sience",
question_string: "What part of the body helps you move?",
choices: {
correct: "Muscles",
wrong: ["Eyes", "Pancreas", "Lungs"]
}
}, {
type:"sience",
question_string: 'What star shines in the day and provides light?',
choices: {
correct: "Sun",
wrong: ["Moon", "Venus", "Mars"]
}
}];
var Quiz = function(quiz_name) {
this.quiz_name = quiz_name;
this.questions = [];
}
Quiz.prototype.add_question = function(question) {
var index_to_add_question = Math.floor(Math.random() * this.questions.length);
this.questions.splice(index_to_add_question, 0, question);
}
Quiz.prototype.render = function(container) {
var self = this;
$('#quiz-results').hide();
$('#quiz-name').text(this.quiz_name);
var question_container = $('<div>').attr('id', 'question').insertAfter('#quiz-name');
function change_question() {
self.questions[current_question_index].render(question_container);
$('#prev-question-button').prop('disabled', current_question_index === 0);
$('#next-question-button').prop('disabled', current_question_index === self.questions.length - 1);
var all_questions_answered = true;
for (var i = 0; i < self.questions.length; i++) {
if (self.questions[i].user_choice_index === null) {
all_questions_answered = false;
break;
}
}
$('#submit-button').prop('disabled', !all_questions_answered);
}
var current_question_index = 0;
change_question();
$('#prev-question-button').click(function() {
if (current_question_index > 0) {
current_question_index--;
change_question();
}
});
$('#next-question-button').click(function() {
if (current_question_index < self.questions.length - 1) {
current_question_index++;
change_question();
}
});
$('#submit-button').click(function() {
let totalScore = 0;
const score = self.questions.reduce((acc, curr) => {
if (curr.user_choice_index === curr.correct_choice_index) {
acc.set(curr.question_type, (acc.get(curr.question_type) || 0) + 1);
totalScore++;
}
return acc;
}, new Map());
var percentage = score / self.questions.length;
console.log(percentage);
var message;
if (percentage === 1) {
message = 'Great job!'
} else if (percentage >= .75) {
message = 'You did alright.'
} else if (percentage >= .5) {
message = 'Better luck next time.'
} else {
message = 'Maybe you should try a little harder.'
}
$('#quiz-results-message').text(message);
const scoreText = Array.from(score.entries())
.map(([type, scoreByType]) => `<b>${type}</b>: ${scoreByType}`)
.join('\n');
$('#quiz-results-score').html('You got <b>' + totalScore + '/' + self.questions.length + '</b> questions correct.\n' + scoreText);
$('#quiz-results').slideDown();
$('#quiz button').slideUp();
});
question_container.bind('user-select-change', function() {
var all_questions_answered = true;
for (var i = 0; i < self.questions.length; i++) {
if (self.questions[i].user_choice_index === null) {
all_questions_answered = false;
break;
}
}
$('#submit-button').prop('disabled', !all_questions_answered);
});
}
var Question = function(question_string, correct_choice, wrong_choices, question_type) {
// Private fields for an instance of a Question object.
this.question_string = question_string;
this.question_type = question_type;
this.choices = [];
this.user_choice_index = null; // Index of the user's choice selection
// Random assign the correct choice an index
this.correct_choice_index = Math.floor(Math.random() * wrong_choices.length + 1);
// Fill in this.choices with the choices
var number_of_choices = wrong_choices.length + 1;
for (var i = 0; i < number_of_choices; i++) {
if (i === this.correct_choice_index) {
this.choices[i] = correct_choice;
} else {
// Randomly pick a wrong choice to put in this index
var wrong_choice_index = Math.floor(Math.random(0, wrong_choices.length));
this.choices[i] = wrong_choices[wrong_choice_index];
// Remove the wrong choice from the wrong choice array so that we don't pick it again
wrong_choices.splice(wrong_choice_index, 1);
}
}
}
Question.prototype.render = function(container) {
var self = this;
var question_string_h2;
if (container.children('h2').length === 0) {
question_string_h2 = $('<h2>').appendTo(container);
} else {
question_string_h2 = container.children('h2').first();
}
question_string_h2.text(this.question_string);
if (container.children('input[type=radio]').length > 0) {
container.children('input[type=radio]').each(function() {
var radio_button_id = $(this).attr('id');
$(this).remove();
container.children('label[for=' + radio_button_id + ']').remove();
});
}
for (var i = 0; i < this.choices.length; i++) {
var choice_radio_button = $('<input>')
.attr('id', 'choices-' + i)
.attr('type', 'radio')
.attr('name', 'choices')
.attr('value', 'choices-' + i)
.attr('checked', i === this.user_choice_index)
.appendTo(container);
var choice_label = $('<label>')
.text(this.choices[i])
.attr('for', 'choices-' + i)
.appendTo(container);
}
$('input[name=choices]').change(function(index) {
var selected_radio_button_value = $('input[name=choices]:checked').val();
self.user_choice_index = parseInt(selected_radio_button_value.substr(selected_radio_button_value.length - 1, 1));
container.trigger('user-select-change');
});
}
$(document).ready(function() {
var quiz = new Quiz('');
for (var i = 0; i < all_questions.length; i++) {
var question = new Question(all_questions[i].question_string, all_questions[i].choices.correct, all_questions[i].choices.wrong, all_questions[i].type);
quiz.add_question(question);
}
var quiz_container = $('#quiz');
quiz.render(quiz_container);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CodePen - Multiple Choice Quiz</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div id="quiz">
<h1 id="quiz-name"></h1>
<button id="submit-button">Submit</button>
<button id="next-question-button">Next</button>
<button id="prev-question-button">Back</button>
<div id="quiz-results">
<p id="quiz-results-message"></p>
<p id="quiz-results-score"></p>
</div>
</div>
<!-- partial -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src="./script.js"></script>
</body>
</html>

Polymers this.$$(selector) (querySelector) for elements inside template repeat

I am simply trying to get an element inside of a template repeat by it's custom attribute. Here is the code:
<dom-module id="order-customize-sauces">
<link rel="stylesheet" href="order-customize-sauces.css"/>
<template>
<center>
<div style="width:100%; height:80px;">
<input class="customize-search" title="Filter The List By A Search Term" placeholder="Search List..." value="{{searchValue::input}}" type="text" on-keyup="filterSelections"></input>
<iron-icon icon="search" style="float:right;margin-right:5px;"></iron-icon>
</div>
<div style="height:300px;width:100%;">
<template id="saucesRepeat" is="dom-repeat" items="{{repeat}}" filter="{{filterSelections(searchValue)}}">
<paper-button data-sauce$="{{item.code}}" class$="button-{{item.SELECTED}}" raised on-click="buttonClick">{{item.name}}</paper-button>
</template>
</div>
</center>
<div style="margin-top:300px"> </div>
</template>
<script src="order-customize-sauces.js"></script>
As you can see I have "data-sauce" attribute that I want to select by. So here is what I have tried with no luck:
Try1:
var repeaters = document.querySelector('template[is="dom-repeat"]')
console.log(repeaters.querySelector("[data-sauce]"));
Try 2:
var items = Polymer.dom(this.$.saucesRepeat).node.__data__.items;
console.log(this.$$("[data-sauce]"));
Try 3:
this.$$("[data-sauce]");
Am I going about this wrong? How can I select an element inside of a template repeat? Also I am not using JQuery so if this could be javascript or polymer specific that would be great!
EDIT:
I have added the full JS code to help with the debugging.
The main function where this is the question is in the "changeButtonColor" function. This is called after a series of functions but it all starts with the "passCurrentItem" function. Also this element is a child of another element which is calling this elements function like the following:
this.$.allSaucesOptions.passCurrentItem(mynewarray);
The "allSaucesOptions" is the id given to the "order-customize-sauces" element.
Full JS Code:
Polymer({
is:"order-customize-sauces",
behaviors: [
Polymer.NeonSharedElementAnimatableBehavior
],
properties:{
animationConfig: {
value: function() {
return {
'entry':[
{
name:'fade-in-animation',
node:this
}],
'exit':[
{
name:'fade-out-animation',
node:this
}]
}
}
},
},
//--------------------------------------------------
ready:function(){
this.repeat = [];
this.currentItem = [];
this.originalArray = [];
},
//--------------------------------------------------
passCurrentList:function(list){
this.originalArray = list;
},
//--------------------------------------------------
passCurrentItem:function(array) {
var i;
this.currentItem.length = 0;
for(var i=0; i < array.length; i++) {
this.currentItem.push({name: array[i].name, code: array[i].code, price: array[i].price, SELECTED: array[i].SELECTED});
}
this.resetRepeatArray(); //everything above works!!
this.updateArray(); //update visually
},
//--------------------------------------------------
resetRepeatArray:function(){ //testing working!!!!
var i;
for(i=0; i < this.originalArray.length; i++) {
this.originalArray[i].SELECTED = 0;
}
this.repeat = this.originalArray;
},
//--------------------------------------------------
updateArray:function() {
var i;
//for(i =0; i < this.repeat.length; i++) {
//this.repeat[i] = {name: this.repeat[i].name, code: this.repeat[i].code, price: this.repeat[i].price, SELECTED: this.repeat[i].SELECTED};
//this.changeButtonColor(0,this.repeat[i].code);
//}
for(i = 0; i < this.currentItem.length; i++) {
//index = this.repeat.map(function(d) { return d['code']; }).indexOf(this.currentItem[i].code);
//this.set("repeat.index",{name: this.currentItem[i].name, code: this.currentItem[i].code,
// price: this.currentItem[i].price, SELECTED: this.currentItem[i].SELECTED});
console.log("set color "+this.currentItem[i].name);
this.changeButtonColor(this.currentItem[i].SELECTED,this.currentItem[i].code); //this isn't working
}
},
//--------------------------------------------------
changeButtonColor:function(number, id) {
var defaultClassValues = " style-scope order-customize-sauces x-scope paper-button-0";
var defaultClass ="button-";
// var items = Polymer.dom(this.$.saucesRepeat).node.__data__.items;
// console.log(this.$$("[data-sauce]"));
// var repeaters = document.querySelector('template[is="dom-repeat"]')
var element = this.$$("[data-sauce]");
console.log(element);
// for(var i=0; i < items.length; i++) {
// if(items[i].code === id) {
// Polymer.dom(this.$.saucesRepeat).node.__data__.items[i].SELECTED = number;
// }
// }
// var element = this.getElementByAttributeValue("data-sauce",id);
// if(element != null){
// element.className = defaultClass+number+defaultClassValues;
// console.log(element.className);
// }
},
//--------------------------------------------------
getElementByAttributeValue: function(attribute, value){
var matchingElements = [];
var allElements = document.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++) {
if (allElements[i].getAttribute(attribute) === value){
// Element exists with attribute. Add to array.
matchingElements.push(allElements[i]);
}
}
return matchingElements[0];
},
//--------------------------------------------------
isNumber:function(selected, number) {
selected = (selected == null)?0:selected;
if(selected == number) {
return false;
}
else {
return true;
}
},
//--------------------------------------------------
removeFromArray:function(itemName) {
var index, i;
for(i = 0; i < this.currentItem.length; i++) {
if(this.currentItem[i].name == itemName || this.currentItem[i].code == itemName) {
index = i;
break;
}
}
if(index > -1) {
this.currentItem.splice(index, 1);
}
},
//--------------------------------------------------
buttonClick:function(e){
var item = e.model.__data__.item;
item.SELECTED = this.setSelectedNumber(item.SELECTED);
var number = item.SELECTED;
if(number > 3) {//original item
this.currentItem[0] = {name: item.name, code: item.code, price: item.price, SELECTED: 4};//change original item to new values
if(this.currentItem[1] != null) { //is there another item selected?
this.removeFromArray(this.currentItem[1].code); //make original item the only thing in array
}
}
else if(number < 4) {//not original item
this.currentItem[0] = {name: this.currentItem[0].name, code: this.currentItem[0].code, price: this.currentItem[0].price, SELECTED: 4};//unselect original item -- don't remove from array
if(this.currentItem[1] == null) { //is this the first time selecting a new item?
this.currentItem.push({name: item.name, code: item.code, price: item.price, SELECTED: item.SELECTED}); //add item to a new slot in array
}
else{
this.currentItem[1] = {name: item.name, code: item.code, price: item.price, SELECTED: item.SELECTED}; //Update array slot with new information
}
}
this.updateArray();
},
//--------------------------------------------------
setSelectedNumber:function(number) {
if(number < 8 && number > 3) {
number += 1;
if(number > 7) {
number = 4;
}
}
if(number < 4 || number == null) {
number = (number == null)?0:number;
number += 1;
if(number > 3) {
number = 0;
}
}
return number;
},
//---------------------------------------------------
filterSelections:function(search) {
this.resetSauceArray();
this.changeButtonColor(this.currentItem[0].SELECTED, this.currentItem[0].code);
if(this.currentItem[1] != null){
this.changeButtonColor(this.currentItem[1].SELECTED, this.currentItem[1].code);
}
return function(item){
var string;
if (!search) return true;
if (!item) return false;
search = search.toUpperCase();
if(item.name && ~item.name.toUpperCase().indexOf(search)) {
return true;
}
else {
return false;
}
}
},
});

Cannot read property `attachEvent` of null

I have a JavaScript file using speechSynthesis.
I keep having this error:
"Cannot read property 'attachEvent' of null" in line 129:
Here it is:
speech.bind = function(event, element, callback) {
**if (element.attachEvent) {**
element.attachEvent('on'+event, callback )
} else if (window.addEventListener) {
element.addEventListener(event, callback ,false);
};
};
I will put in here the role code in here so anyone can check the entire JavaScript:
var speech_def = speech_def || {
container: "#glb-materia"
,insert_before: true
,ico: "http://edg-1-1242075393.us-east-1.elb.amazonaws.com/speech/listen_icon.jpg"
,bt_txt: "OUÇA A REPORTAGEM"
,bt_stop: "PARAR!"
,source: ".materia-conteudo"
,ignore: [
".foto-legenda"
,".frase-materia"
,"script"
,"style"
,"#speech"
,".sub-header"
,".chamada-materia"
,".data-autor"
,".box-tags"
,".saibamais"
]
};
var speech = speech || {};
//Polyfill remove()
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
};
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for (var i = 0, len = this.length; i < len; i++) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
};
//Polyfill innerText
if ( (!('innerText' in document.createElement('a'))) && ('getSelection' in window) ) {
HTMLElement.prototype.__defineGetter__("innerText", function() {
var selection = window.getSelection(),
ranges = [],
str;
for (var i = 0; i < selection.rangeCount; i++) {
ranges[i] = selection.getRangeAt(i);
}
selection.removeAllRanges();
selection.selectAllChildren(this);
str = selection.toString();
selection.removeAllRanges();
for (var i = 0; i < ranges.length; i++) {
selection.addRange(ranges[i]);
}
return str;
})
}
speech.iOS = /(iPad|iPhone|iPod)/g.test( navigator.userAgent );
speech.Android = /(Android)/g.test( navigator.userAgent );
speech.include = function() {
var bt = ""
bt += '<div id="speech" '
+'title="'+speech_def.bt_txt+'" '
+'style="'
+'display: none; '
+'margin: 5px; '
+'font-size: 12px; '
+'font-style: italic; '
+'color: #bbbbbb; '
+'cursor: pointer;"'
+'>';
bt += '<img style="width: 25px; height: 25px;" src="'+speech_def.ico+'"> ';
bt += '<i style="vertical-align: top; line-height: 28px;">'+speech_def.bt_txt+'</i>';
bt += '</div>';
var button = document.createElement("SPAN");
button.innerHTML = bt;
var box = document.querySelectorAll(speech_def.container)[0];
if (speech_def.insert_before) {
box.insertBefore(button,box.firstChild);
} else {
box.appendChild(button)
};
};
speech.stop = function() {
window.speechSynthesis.cancel();
}
speech.stop();
speech.content = function() {
var result = "";
var boxes = speech_def.source.split(",");
boxes.reverse();
for (var n = boxes.length - 1; n >= 0; n--) {
var doc = document.querySelector(boxes[n]);
if (doc) {
doc = doc.cloneNode(true);
for (var i = speech_def.ignore.length - 1; i >= 0; i--) {
var els = doc.querySelectorAll(speech_def.ignore[i]);
for (var j = els.length - 1; j >= 0; j--) {
els[j].remove();
};
};
result += "." + doc.innerText;
};
};
return result;
};
speech.start_speech = function() {
var content = speech.content();
// Note: some voices don't support altering params
if (!speech.Android) speech.msg.voice = speech.voices[0];
// msg.voiceURI = 'native';
speech.msg.volume = speech.iOS?1:1; // 0 to 1
speech.msg.rate = speech.iOS?0.6:1; // 0.1 to 10
speech.msg.pitch = speech.iOS?1:1; // 0 to 2
speech.msg.text = content;
speech.msg.lang = 'pt-BR';
speech.msg.onend = function(e) {
// console.log('Finished in ' + event.elapsedTime + ' seconds.');
};
window.speechSynthesis.speak(speech.msg);
};
speech.read = function(){}; //chrome problem
speech.bind = function(event, element, callback) {
if (element.attachEvent) {
element.attachEvent('on'+event, callback )
} else if (window.addEventListener) {
element.addEventListener(event, callback ,false);
};
};
speech.click = function(e){
event.stopPropagation()
if (window.event) window.event.cancelBubble = true;
var control = document.getElementById("speech");
var label;
if (window.speechSynthesis.speaking) {
label = speech_def.bt_txt;
speech.stop();
} else {
label = speech_def.bt_stop;
speech.start_speech();
};
control.querySelector("i").innerHTML = label;
}
speech.bind_button = function() {
var control = document.getElementById("speech");
speech.bind("click",control,speech.click);
};
speech.show_button = function() {
if (!speech.on_page) {
speech.on_page = true;
speech.include();
speech.bind_button();
};
var control = document.getElementById("speech");
control.style.display="inline-block";
};
speech.test_portuguese = function() {
speech.voices = [];
window.speechSynthesis.getVoices().forEach(function(voice) {
if (voice.lang == "pt-BR") {
speech.voices.push(voice);
};
});
if (speech.Android) {
var control = document.getElementById("speech");
var complement = (speech.voices.length > 0)?"*":"";
// control.querySelector("i").innerHTML = "OUÇA A REPORTAGEM"+complement;
return true;
} else {
return (speech.voices.length > 0);
};
};
speech.start = function() {
if ('speechSynthesis' in window) {
speech.msg = new SpeechSynthesisUtterance();
if (speech.test_portuguese()) {
speech.show_button();
} else {
window.speechSynthesis.onvoiceschanged = function() {
if (speech.test_portuguese()) {
speech.show_button();
};
};
};
speech.bind_button();
};
};
speech.start();
speech.bind("load",window,speech.start)
How can i solve this problem?
Thanks so much.
The problem is that element is null at that point.
Probably, because there is no element with id="speech", so control in null in this code:
speech.bind_button = function() {
var control = document.getElementById("speech");
speech.bind("click",control,speech.click);
};

Simple pagination in javascript

I am trying to make pagination for my site. (http://anuntorhei.md)
CODE:
var someVar = 50;
function someStupidFunction() {
if (objJson.length > 50) {
document.getElementById("nextPage").style.visibility = "visible";
}
if (someVar <= 50) {
document.getElementById("prevPage").style.visibility ="hidden";
} else {
document.getElementById("prevPage").style.visibility = "visible";
}
}
function nextPage() {
document.getElementById("listingTable").innerHTML = "";
if (someVar < objJson.length) {
document.getElementById("nextPage").style.visibility = "visible";
} else {
document.getElementById("nextPage").style.visibility = "hidden";
}
for (var i = someVar - 50; i < someVar; i++) {
document.getElementById("listingTable").innerHTML += objJson[i].adName + "<br>";
}
someVar += 50;
document.getElementById("prevPage").style.visibility = "visible";
}
function prevPage() {
document.getElementById("listingTable").innerHTML = "";
if (someVar > 50) {
document.getElementById("prevPage").style.visibility = "visible";
} else {
document.getElementById("prevPage").style.visibility = "hidden";
}
for (var i = someVar - 50; i < someVar; i++) {
document.getElementById("listingTable").innerHTML += objJson[i].adName + "<br>";
}
someVar -= 50;
document.getElementById("nextPage").style.visibility = "visible";
}
But I can't understand how to "hide" nextPage button when someVar is bigger than objJson.length.
And when I reach the "end", nextPage button disappear after than objJson is smaller than someVar. What is wrong in this code?
How can I change it to make it perfect? Sorry for my bad English, can't explain all what I need, hope you understand what I need!
I'll address any questions you have... but here is an improved pattern you should follow to reduce code duplication.
As a sidenote though, you should consider not doing pagination on client-side. Since if you have a huge dataset, it would mean you need to download all the data before your page loads. Better to implement server-side pagination instead.
Fiddle: http://jsfiddle.net/Lzp0dw83/
HTML
<div id="listingTable"></div>
Prev
Next
page: <span id="page"></span>
Javascript (put anywhere):
var current_page = 1;
var records_per_page = 2;
var objJson = [
{ adName: "AdName 1"},
{ adName: "AdName 2"},
{ adName: "AdName 3"},
{ adName: "AdName 4"},
{ adName: "AdName 5"},
{ adName: "AdName 6"},
{ adName: "AdName 7"},
{ adName: "AdName 8"},
{ adName: "AdName 9"},
{ adName: "AdName 10"}
]; // Can be obtained from another source, such as your objJson variable
function prevPage()
{
if (current_page > 1) {
current_page--;
changePage(current_page);
}
}
function nextPage()
{
if (current_page < numPages()) {
current_page++;
changePage(current_page);
}
}
function changePage(page)
{
var btn_next = document.getElementById("btn_next");
var btn_prev = document.getElementById("btn_prev");
var listing_table = document.getElementById("listingTable");
var page_span = document.getElementById("page");
// Validate page
if (page < 1) page = 1;
if (page > numPages()) page = numPages();
listing_table.innerHTML = "";
for (var i = (page-1) * records_per_page; i < (page * records_per_page); i++) {
listing_table.innerHTML += objJson[i].adName + "<br>";
}
page_span.innerHTML = page;
if (page == 1) {
btn_prev.style.visibility = "hidden";
} else {
btn_prev.style.visibility = "visible";
}
if (page == numPages()) {
btn_next.style.visibility = "hidden";
} else {
btn_next.style.visibility = "visible";
}
}
function numPages()
{
return Math.ceil(objJson.length / records_per_page);
}
window.onload = function() {
changePage(1);
};
UPDATE 2014/08/27
There is a bug above, where the for loop errors out when a particular page (the last page usually) does not contain records_per_page number of records, as it tries to access a non-existent index.
The fix is simple enough, by adding an extra checking condition into the for loop to account for the size of objJson:
Updated fiddle: http://jsfiddle.net/Lzp0dw83/1/
for (var i = (page-1) * records_per_page; i < (page * records_per_page) && i < objJson.length; i++)
I created a class structure for collections in general that would meet this requirement. and it looks like this:
class Collection {
constructor() {
this.collection = [];
this.index = 0;
}
log() {
return console.log(this.collection);
}
push(value) {
return this.collection.push(value);
}
pushAll(...values) {
return this.collection.push(...values);
}
pop() {
return this.collection.pop();
}
shift() {
return this.collection.shift();
}
unshift(value) {
return this.collection.unshift(value);
}
unshiftAll(...values) {
return this.collection.unshift(...values);
}
remove(index) {
return this.collection.splice(index, 1);
}
add(index, value) {
return this.collection.splice(index, 0, value);
}
replace(index, value) {
return this.collection.splice(index, 1, value);
}
clear() {
this.collection.length = 0;
}
isEmpty() {
return this.collection.length === 0;
}
viewFirst() {
return this.collection[0];
}
viewLast() {
return this.collection[this.collection.length - 1];
}
current(){
if((this.index <= this.collection.length - 1) && (this.index >= 0)){
return this.collection[this.index];
}
else{
return `Object index exceeds collection range.`;
}
}
next() {
this.index++;
this.index > this.collection.length - 1 ? this.index = 0 : this.index;
return this.collection[this.index];
}
previous(){
this.index--;
this.index < 0 ? (this.index = this.collection.length-1) : this.index;
return this.collection[this.index];
}
}
...and essentially what you would do is have a collection of arrays of whatever length for your pages pushed into the class object, and then use the next() and previous() functions to display whatever 'page' (index) you wanted to display. Would essentially look like this:
let books = new Collection();
let firstPage - [['dummyData'], ['dummyData'], ['dummyData'], ['dummyData'], ['dummyData'],];
let secondPage - [['dumberData'], ['dumberData'], ['dumberData'], ['dumberData'], ['dumberData'],];
books.pushAll(firstPage, secondPage); // loads each array individually
books.current() // display firstPage
books.next() // display secondPage
A simple client-side pagination example where data is fetched only once at page loading.
// dummy data
const myarr = [{ "req_no": 1, "title": "test1" },
{ "req_no": 2, "title": "test2" },
{ "req_no": 3, "title": "test3" },
{ "req_no": 4, "title": "test4" },
{ "req_no": 5, "title": "test5" },
{ "req_no": 6, "title": "test6" },
{ "req_no": 7, "title": "test7" },
{ "req_no": 8, "title": "test8" },
{ "req_no": 9, "title": "test9" },
{ "req_no": 10, "title": "test10" },
{ "req_no": 11, "title": "test11" },
{ "req_no": 12, "title": "test12" },
{ "req_no": 13, "title": "test13" },
{ "req_no": 14, "title": "test14" },
{ "req_no": 15, "title": "test15" },
{ "req_no": 16, "title": "test16" },
{ "req_no": 17, "title": "test17" },
{ "req_no": 18, "title": "test18" },
{ "req_no": 19, "title": "test19" },
{ "req_no": 20, "title": "test20" },
{ "req_no": 21, "title": "test21" },
{ "req_no": 22, "title": "test22" },
{ "req_no": 23, "title": "test23" },
{ "req_no": 24, "title": "test24" },
{ "req_no": 25, "title": "test25" },
{ "req_no": 26, "title": "test26" }];
// on page load collect data to load pagination as well as table
const data = { "req_per_page": document.getElementById("req_per_page").value, "page_no": 1 };
// At a time maximum allowed pages to be shown in pagination div
const pagination_visible_pages = 4;
// hide pages from pagination from beginning if more than pagination_visible_pages
function hide_from_beginning(element) {
if (element.style.display === "" || element.style.display === "block") {
element.style.display = "none";
} else {
hide_from_beginning(element.nextSibling);
}
}
// hide pages from pagination ending if more than pagination_visible_pages
function hide_from_end(element) {
if (element.style.display === "" || element.style.display === "block") {
element.style.display = "none";
} else {
hide_from_beginning(element.previousSibling);
}
}
// load data and style for active page
function active_page(element, rows, req_per_page) {
var current_page = document.getElementsByClassName('active');
var next_link = document.getElementById('next_link');
var prev_link = document.getElementById('prev_link');
var next_tab = current_page[0].nextSibling;
var prev_tab = current_page[0].previousSibling;
current_page[0].className = current_page[0].className.replace("active", "");
if (element === "next") {
if (parseInt(next_tab.text).toString() === 'NaN') {
next_tab.previousSibling.className += " active";
next_tab.setAttribute("onclick", "return false");
} else {
next_tab.className += " active"
render_table_rows(rows, parseInt(req_per_page), parseInt(next_tab.text));
if (prev_link.getAttribute("onclick") === "return false") {
prev_link.setAttribute("onclick", `active_page('prev',\"${rows}\",${req_per_page})`);
}
if (next_tab.style.display === "none") {
next_tab.style.display = "block";
hide_from_beginning(prev_link.nextSibling)
}
}
} else if (element === "prev") {
if (parseInt(prev_tab.text).toString() === 'NaN') {
prev_tab.nextSibling.className += " active";
prev_tab.setAttribute("onclick", "return false");
} else {
prev_tab.className += " active";
render_table_rows(rows, parseInt(req_per_page), parseInt(prev_tab.text));
if (next_link.getAttribute("onclick") === "return false") {
next_link.setAttribute("onclick", `active_page('next',\"${rows}\",${req_per_page})`);
}
if (prev_tab.style.display === "none") {
prev_tab.style.display = "block";
hide_from_end(next_link.previousSibling)
}
}
} else {
element.className += "active";
render_table_rows(rows, parseInt(req_per_page), parseInt(element.text));
if (prev_link.getAttribute("onclick") === "return false") {
prev_link.setAttribute("onclick", `active_page('prev',\"${rows}\",${req_per_page})`);
}
if (next_link.getAttribute("onclick") === "return false") {
next_link.setAttribute("onclick", `active_page('next',\"${rows}\",${req_per_page})`);
}
}
}
// Render the table's row in table request-table
function render_table_rows(rows, req_per_page, page_no) {
const response = JSON.parse(window.atob(rows));
const resp = response.slice(req_per_page * (page_no - 1), req_per_page * page_no)
$('#request-table').empty()
$('#request-table').append('<tr><th>Index</th><th>Request No</th><th>Title</th></tr>');
resp.forEach(function (element, index) {
if (Object.keys(element).length > 0) {
const { req_no, title } = element;
const td = `<tr><td>${++index}</td><td>${req_no}</td><td>${title}</td></tr>`;
$('#request-table').append(td)
}
});
}
// Pagination logic implementation
function pagination(data, myarr) {
const all_data = window.btoa(JSON.stringify(myarr));
$(".pagination").empty();
if (data.req_per_page !== 'ALL') {
let pager = `<a href="#" id="prev_link" onclick=active_page('prev',\"${all_data}\",${data.req_per_page})>«</a>` +
`<a href="#" class="active" onclick=active_page(this,\"${all_data}\",${data.req_per_page})>1</a>`;
const total_page = Math.ceil(parseInt(myarr.length) / parseInt(data.req_per_page));
if (total_page < pagination_visible_pages) {
render_table_rows(all_data, data.req_per_page, data.page_no);
for (let num = 2; num <= total_page; num++) {
pager += `<a href="#" onclick=active_page(this,\"${all_data}\",${data.req_per_page})>${num}</a>`;
}
} else {
render_table_rows(all_data, data.req_per_page, data.page_no);
for (let num = 2; num <= pagination_visible_pages; num++) {
pager += `<a href="#" onclick=active_page(this,\"${all_data}\",${data.req_per_page})>${num}</a>`;
}
for (let num = pagination_visible_pages + 1; num <= total_page; num++) {
pager += `<a href="#" style="display:none;" onclick=active_page(this,\"${all_data}\",${data.req_per_page})>${num}</a>`;
}
}
pager += `<a href="#" id="next_link" onclick=active_page('next',\"${all_data}\",${data.req_per_page})>»</a>`;
$(".pagination").append(pager);
} else {
render_table_rows(all_data, myarr.length, 1);
}
}
//calling pagination function
pagination(data, myarr);
// trigger when requests per page dropdown changes
function filter_requests() {
const data = { "req_per_page": document.getElementById("req_per_page").value, "page_no": 1 };
pagination(data, myarr);
}
.box {
float: left;
padding: 50px 0px;
}
.clearfix::after {
clear: both;
display: table;
}
.options {
margin: 5px 0px 0px 0px;
float: left;
}
.pagination {
float: right;
}
.pagination a {
color: black;
float: left;
padding: 8px 16px;
text-decoration: none;
transition: background-color .3s;
border: 1px solid #ddd;
margin: 0 4px;
}
.pagination a.active {
background-color: #4CAF50;
color: white;
border: 1px solid #4CAF50;
}
.pagination a:hover:not(.active) {
background-color: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<table id="request-table">
</table>
</div>
<div class="clearfix">
<div class="box options">
<label>Requests Per Page: </label>
<select id="req_per_page" onchange="filter_requests()">
<option>5</option>
<option>10</option>
<option>ALL</option>
</select>
</div>
<div class="box pagination">
</div>
</div>
Below is the pagination logic as a function
function Pagination(pageEleArr, numOfEleToDisplayPerPage) {
this.pageEleArr = pageEleArr;
this.numOfEleToDisplayPerPage = numOfEleToDisplayPerPage;
this.elementCount = this.pageEleArr.length;
this.numOfPages = Math.ceil(this.elementCount / this.numOfEleToDisplayPerPage);
const pageElementsArr = function (arr, eleDispCount) {
const arrLen = arr.length;
const noOfPages = Math.ceil(arrLen / eleDispCount);
let pageArr = [];
let perPageArr = [];
let index = 0;
let condition = 0;
let remainingEleInArr = 0;
for (let i = 0; i < noOfPages; i++) {
if (i === 0) {
index = 0;
condition = eleDispCount;
}
for (let j = index; j < condition; j++) {
perPageArr.push(arr[j]);
}
pageArr.push(perPageArr);
if (i === 0) {
remainingEleInArr = arrLen - perPageArr.length;
} else {
remainingEleInArr = remainingEleInArr - perPageArr.length;
}
if (remainingEleInArr > 0) {
if (remainingEleInArr > eleDispCount) {
index = index + eleDispCount;
condition = condition + eleDispCount;
} else {
index = index + perPageArr.length;
condition = condition + remainingEleInArr;
}
}
perPageArr = [];
}
return pageArr;
}
this.display = function (pageNo) {
if (pageNo > this.numOfPages || pageNo <= 0) {
return -1;
} else {
console.log('Inside else loop in display method');
console.log(pageElementsArr(this.pageEleArr, this.numOfEleToDisplayPerPage));
console.log(pageElementsArr(this.pageEleArr, this.numOfEleToDisplayPerPage)[pageNo - 1]);
return pageElementsArr(this.pageEleArr, this.numOfEleToDisplayPerPage)[pageNo - 1];
}
}
}
const p1 = new Pagination(['a', 'b', 'c', 'd', 'e', 'f', 'g'], 3);
console.log(p1.elementCount);
console.log(p1.pageEleArr);
console.log(p1.numOfPages);
console.log(p1.numOfEleToDisplayPerPage);
console.log(p1.display(3));
This is the best one for me so far, it will include ´...´ at specific offset
function pages(current_page, last_page, onSides = 3) {
// pages
let pages = [];
// Loop through
for (let i = 1; i <= last_page; i++) {
// Define offset
let offset = (i == 1 || last_page) ? onSides + 1 : onSides;
// If added
if (i == 1 || (current_page - offset <= i && current_page + offset >= i) ||
i == current_page || i == last_page) {
pages.push(i);
} else if (i == current_page - (offset + 1) || i == current_page + (offset + 1)) {
pages.push('...');
}
}
return pages;
}
i am assuming you will display 10 data in every page
HTML:-
<!DOCTYPE html>
<html>
<head>
<title>pagination</title>
<link rel="stylesheet" href="pathofcssfile.css">
</head>
<body>
<div>
<table id="user"></table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul>
<li value="1">1</li>
<li value="2">2</li>
<li value="3">3</li>
<li value="4">4</li>
<li value="5">5</li>
<li value="6">6</li>
<li value="7">7</li>
<li value="8">8</li>
<li value="9">9</li>
<li value="10">10</li>
</ul>
<script src="pathnameofjsfile.js" type="text/javascript"></script>
</body>
</html>
JS:-
var xhr = new XMLHttpRequest();
xhr.open('GET',"https://jsonplaceholder.typicode.com/albums",true);
xhr.send();
var udata;
xhr.onload = function()
{
if(this.status == 200)
{
var userdata = JSON.parse(this.responseText);
console.log(userdata);
udata = userdata;
data(1);
}
}
$("li").click(function ()
{
var a = $(this).attr("value");
console.log("value li "+ a);
data(a);
});
function data(a)
{
var output = "";
for(i=((a-1)*10);i<(a*10);i++)
{
output +='<tr>'+
'<td>'+ udata[i].userId + '</td>'+
'<td>'+ udata[i].id + '</td>'+
'<td>'+ udata[i].title + '</td>'+ '<br>'
'</tr>';
}
document.getElementById('user').innerHTML = output;
}
CSS:-
ul{
display: flex;
list-style-type:none;
padding: 20px;
}
li{
padding: 20px;
}
td,tr{
padding: 10px;
}
Following is the Logic which accepts count from user and performs pagination in Javascript.
It prints alphabets. Hope it helps!!. Thankyou.
/*
*****
USER INPUT : NUMBER OF SUGGESTIONS.
*****
*/
var recordSize = prompt('please, enter the Record Size');
console.log(recordSize);
/*
*****
POPULATE SUGGESTIONS IN THE suggestion_set LIST.
*****
*/
var suggestion_set = [];
counter = 0;
asscicount = 65;
do{
if(asscicount <= 90){
var temp = String.fromCharCode(asscicount);
suggestion_set.push(temp);
asscicount += 1;
}else{
asscicount = 65;
var temp = String.fromCharCode(asscicount);
suggestion_set.push(temp);
asscicount += 1;
}
counter += 1;
}while(counter < recordSize);
console.log(suggestion_set);
/*
*****
LOGIC FOR PAGINATION
*****
*/
var totalRecords = recordSize, pageSize = 6;
var q = Math.floor(totalRecords/pageSize);
var r = totalRecords%pageSize;
var itr = 1;
if(r==0 ||r==1 ||r==2) {
itr=q;
}
else {
itr=q+1;
}
console.log(itr);
var output = "", pageCnt=1, newPage=false;
if(totalRecords <= pageSize+2) {
output += "\n";
for(var i=0; i < totalRecords; i++){
output += suggestion_set[i] + "\t";
}
}
else {
output += "\n";
for(var i=0; i<totalRecords; i++) {
//output += (i+1) + "\t";
if(pageCnt==1){
output += suggestion_set[i] + "\t";
if((i+1)==(pageSize+1)) {
output += "Next" + "\t";
pageCnt++;
newPage=true;
}
}
else {
if(newPage) {
output += "\n" + "Previous" + "\t";
newPage = false;
}
output += suggestion_set[i] + "\t";
if((i+1)==(pageSize*pageCnt+1) && (pageSize*pageCnt+1)<totalRecords) {
if((i+2) == (pageSize*pageCnt+2) && pageCnt==itr) {
output += (suggestion_set[i] + 1) + "\t";
break;
}
else {
output += "Next" + "\t";
pageCnt++;
newPage=true;
}
}
}
}
}
console.log(output);
file:icons.svg
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-triangle-left" viewBox="0 0 20 20">
<title>triangle-left</title>
<path d="M14 5v10l-9-5 9-5z"></path>
</symbol>
<symbol id="icon-triangle-right" viewBox="0 0 20 20">
<title>triangle-right</title>
<path d="M15 10l-9 5v-10l9 5z"></path>
</symbol>
</defs>
</svg>
file: style.css
.results__btn--prev{
float: left;
flex-direction: row-reverse; }
.results__btn--next{
float: right; }
file index.html:
<body>
<form class="search">
<input type="text" class="search__field" placeholder="Search over 1,000,000 recipes...">
<button class="btn search__btn">
<svg class="search__icon">
<use href="img/icons.svg#icon-magnifying-glass"></use>
</svg>
<span>Search</span>
</button>
</form>
<div class="results">
<ul class="results__list">
</ul>
<div class="results__pages">
</div>
</div>
</body>
file: searchView.js
export const element = {
searchForm:document.querySelector('.search'),
searchInput: document.querySelector('.search__field'),
searchResultList: document.querySelector('.results__list'),
searchRes:document.querySelector('.results'),
searchResPages:document.querySelector('.results__pages')
}
export const getInput = () => element.searchInput.value;
export const clearResults = () =>{
element.searchResultList.innerHTML=``;
element.searchResPages.innerHTML=``;
}
export const clearInput = ()=> element.searchInput.value = "";
const limitRecipeTitle = (title, limit=17)=>{
const newTitle = [];
if(title.length>limit){
title.split(' ').reduce((acc, cur)=>{
if(acc+cur.length <= limit){
newTitle.push(cur);
}
return acc+cur.length;
},0);
}
return `${newTitle.join(' ')} ...`
}
const renderRecipe = recipe =>{
const markup = `
<li>
<a class="results__link" href="#${recipe.recipe_id}">
<figure class="results__fig">
<img src="${recipe.image_url}" alt="${limitRecipeTitle(recipe.title)}">
</figure>
<div class="results__data">
<h4 class="results__name">${recipe.title}</h4>
<p class="results__author">${recipe.publisher}</p>
</div>
</a>
</li>
`;
var htmlObject = document.createElement('div');
htmlObject.innerHTML = markup;
element.searchResultList.insertAdjacentElement('beforeend',htmlObject);
}
const createButton = (page, type)=>`
<button class="btn-inline results__btn--${type}" data-goto=${type === 'prev'? page-1 : page+1}>
<svg class="search__icon">
<use href="img/icons.svg#icon-triangle-${type === 'prev'? 'left' : 'right'}}"></use>
</svg>
<span>Page ${type === 'prev'? page-1 : page+1}</span>
</button>
`
const renderButtons = (page, numResults, resultPerPage)=>{
const pages = Math.ceil(numResults/resultPerPage);
let button;
if(page == 1 && pages >1){
//button to go to next page
button = createButton(page, 'next');
}else if(page<pages){
//both buttons
button = `
${createButton(page, 'prev')}
${createButton(page, 'next')}`;
}
else if (page === pages && pages > 1){
//Only button to go to prev page
button = createButton(page, 'prev');
}
element.searchResPages.insertAdjacentHTML('afterbegin', button);
}
export const renderResults = (recipes, page=1, resultPerPage=10) =>{
/*//recipes.foreach(el=>renderRecipe(el))
//or foreach will automatically call the render recipes
//recipes.forEach(renderRecipe)*/
const start = (page-1)*resultPerPage;
const end = page * resultPerPage;
recipes.slice(start, end).forEach(renderRecipe);
renderButtons(page, recipes.length, resultPerPage);
}
file: Search.js
export default class Search{
constructor(query){
this.query = query;
}
async getResults(){
try{
const res = await axios(`https://api.com/api/search?&q=${this.query}`);
this.result = res.data.recipes;
//console.log(this.result);
}catch(error){
alert(error);
}
}
}
file: Index.js
onst state = {};
const controlSearch = async()=>{
const query = searchView.getInput();
if (query){
state.search = new Search(query);
searchView.clearResults();
searchView.clearInput();
await state.search.getResults();
searchView.renderResults(state.search.result);
}
}
//event listner to the parent object to delegate the event
element.searchForm.addEventListener('submit', event=>{
console.log("submit search");
event.preventDefault();
controlSearch();
});
element.searchResPages.addEventListener('click', e=>{
const btn = e.target.closest('.btn-inline');
if(btn){
const goToPage = parseInt(btn.dataset.goto, 10);//base 10
searchView.clearResults();
searchView.renderResults(state.search.result, goToPage);
}
});
Just create and save a page token in global variable with window.nextPageToken. Send this to API server everytime you make a request and have it return the next one with response and you can easily keep track of last token. The below is an example how you can move forward and backward from search results. The key is the offset you send to API based on the nextPageToken that you have saved:
function getPrev() {
var offset = Number(window.nextPageToken) - limit * 2;
if (offset < 0) {
offset = 0;
}
window.nextPageToken = offset;
if (canSubmit(searchForm, offset)) {
searchForm.submit();
}
}
function getNext() {
var offset = Number(window.nextPageToken);
window.nextPageToken = offset;
if (canSubmit(searchForm, offset)) {
searchForm.submit();
}
}
var data = [1, 2, 3, 4, 5, 6, 7];
var item_per_page = 3;
var current_page = 1;
var pagination = {
total: data.length,
per_page: item_per_page,
current_page: current_page,
last_page: Math.ceil(data.length / item_per_page),
from: (current_page - 1) * item_per_page + 1,
to: current_page * item_per_page,
};
changePage();
function changePage() {
var temp = [];
for (let i = pagination.from; i <= pagination.to; i++) {
temp.push(data[i - 1]);
}
document.getElementById("result").innerHTML = temp.filter(x => x);
console.log(pagination);
}
function up() {
if (pagination.from == 1) {
return false;
}
pagination.current_page -= 1;
pagination.from = (pagination.current_page - 1) * item_per_page + 1;
pagination.to = pagination.current_page * item_per_page;
changePage();
}
function down() {
if (pagination.last_page == pagination.current_page) {
return false;
}
pagination.current_page += 1;
pagination.from = (pagination.current_page - 1) * item_per_page + 1;
pagination.to = pagination.current_page * item_per_page;
changePage();
}
console.log(pagination);
button {
width: 100px;
}
<div style="display: grid">
<button type="button" onclick="up()">Up</button>
<div id="result"></div>
<button type="button" onclick="down()">Down</button>
</div>
You can use the code from this minimal plugin.
https://www.npmjs.com/package/paginator-js
Array.prototype.paginate = function(pageNumber, itemsPerPage){
pageNumber = Number(pageNumber)
itemsPerPage = Number(itemsPerPage)
pageNumber = (pageNumber < 1 || isNaN(pageNumber)) ? 1 : pageNumber
itemsPerPage = (itemsPerPage < 1 || isNaN(itemsPerPage)) ? 1 : itemsPerPage
var start = ((pageNumber - 1) * itemsPerPage)
var end = start + itemsPerPage
var loopCount = 0
var result = {
data: [],
end: false
}
for(loopCount = start; loopCount < end; loopCount++){
this[loopCount] && result.data.push(this[loopCount]);
}
if(loopCount == this.length){
result.end = true
}
return result
}

javascript: How to build a function that refers to the variables in my Quiz app?

I recently built a small quiz application, it currently only has two questions. After all the questions are finished I would like for the app to present a page that says "You made it here" (eventually I'll add more). However for some reason the final-function feedback of this code is not working. Where am I going wrong?
$(document).ready(function () {
var questions = [
{question: "Who is Zack Morris?",
choices: ['images/ACslater.jpg','images/CarltonBanks.jpeg','images/ZachMorris.jpg'],
quesNum: 1,
correctAns: 2},
{question: "Who is Corey Matthews?",
choices: ['images/CoryMatthews.jpeg','images/EdAlonzo.jpg','images/Shawnhunter.jpg'],
quesNum: 2,
correctAns: 1},
];
var userAnswer //THis needs to be looked into
var counter = 0;
var score = 0;
var html_string = '';
var string4end = ''
//function to loop choices in HTML, updates counter, checks answer
var update_html = function(currentQuestion) {
// put current question into a variable for convenience.
// put the question string between paragraph tags
html_string = '<p>' + currentQuestion.question + '</p>';
// create an unordered list for the choices
html_string += '<ul>';
// loop through the choices array
for (var j = 0; j < currentQuestion.choices.length; j++) {
// put the image as a list item
html_string += '<li><img src="' + currentQuestion.choices[j] + '"></li>';
}
html_string += '</ul>';
$('.setImg').html(html_string);
}
update_html(questions[0]);
$('.setImg li').on('click', function (e) {
userAnswer = $(this).index();
checkAnswer();
counter++;
update_html(questions[counter]);
$('#score').html(score);
showFinalFeedback();
});
//function to identify right question
function checkAnswer ()
{
if (userAnswer === questions[counter].correctAns)
{
score=+100;
}
}
function showFinalFeedback ()
{
if (counter === (questions.length+1))
{
string4end = '<p>' + 'You made it here!!!!' + '</p>';
$('.setImg').html(string4end);
}
}
});
I agree with Vector that you either should start with 1 as Counter initialization or, to check
if (counter < questions.length) {
return;
}
alert('You \'ve made it till here');
I also rewrote it in a form of a jquery plugin, maybe a handy comparison to your way of working?
jsfiddle: http://jsfiddle.net/DEb7J/
;(function($) {
function Question(options) {
if (typeof options === 'undefined') {
return;
}
this.chosen = -1;
this.question = options.question;
this.options = options.options;
this.correct = options.correct;
this.toString = function() {
var msg = '<h3><i>' + this.question + '</i></h3>';
for (var i = 0; i < this.options.length; i++) {
msg += '<a id="opt' + i + '" class="answer toggleOff" onclick="$(this.parentNode).data(\'quizMaster\').toggle(' + i + ')">' + this.options[i] + '</a>';
}
return msg;
};
this.toggle = function(i) {
var el = $('#opt' + i);
if ($(el).hasClass('toggleOff')) {
$(el).removeClass('toggleOff');
$(el).addClass('toggleOn');
} else {
$(el).removeClass('toggleOn');
$(el).addClass('toggleOff');
}
};
}
function Quiz(elem, options) {
this.element = $(elem);
this.lastQuestion = -1;
this.questions = [];
this.correct = 0;
if (typeof options !== 'undefined' && typeof options.questions !== undefined) {
for (var i = 0; i < options.questions.length; i++) {
this.questions.push(new Question(options.questions[i]));
}
}
this.start = function() {
this.lastQuestion = -1;
this.element.html('');
for (var i = 0; i < this.questions.length; i++) {
this.questions[i].chosen = -1;
}
this.correct = 0;
this.next();
};
this.next = function() {
if (this.lastQuestion >= 0) {
var p = this.questions[this.lastQuestion];
if (p.chosen === -1) {
alert('Answer the question first!');
return false;
}
if (p.chosen === p.correct) {
this.correct++;
}
$(this.element).html('');
}
this.lastQuestion++;
if (this.lastQuestion < this.questions.length) {
var q = this.questions[this.lastQuestion];
$(this.element).html(q.toString());
console.log(q.toString());
} else {
alert('you replied correct on ' + this.correct + ' out of ' + this.questions.length + ' questions');
this.start();
}
};
this.toggle = function(i) {
if (this.lastQuestion < this.questions.length) {
var q = this.questions[this.lastQuestion];
q.toggle(q.chosen);
q.toggle(i);
q.chosen = i;
}
};
}
$.fn.quizMaster = function(options) {
if (!this.length || typeof this.selector === 'undefined') {
return;
}
var quiz = new Quiz($(this), options);
quiz.start();
$(this).data('quizMaster', quiz);
$('#btnConfirmAnswer').on('click', function(e) {
e.preventDefault();
quiz.next();
});
};
}(jQuery));
$(function() {
$('#millionaire').quizMaster({
questions: [
{
question: 'Where are the everglades?',
options: ['Brazil','France','USA','South Africa'],
correct: 2
},
{
question: 'Witch sport uses the term "Homerun"?',
options: ['Basketball','Baseball','Hockey','American Football'],
correct: 1
}
]
});
});
Hey guys thanks for your help. I was able to use the following work around to ensure everything worked:
$('.setImg').on('click', 'li', function () {
userAnswer = $(this).index();
checkAnswer();
counter++;
$('#score').html(score);
if (counter < questions.length)
{
update_html(questions[counter]);
}
else{
showFinalFeedback();
}
});

Categories

Resources