I'm new with Javascript, and coding in general. I have been trying to create a code that has an array full of images. Then, when a button is clicked, it will generate a number that links in with the array, and then display the image. Furthermore, I want it to then not use that number/image again until all are used up.
For now I am just starting with having an array, randomly generating a number and displaying the image. I have scoured this forum, gone on SEVERAL posts and tried numerous codes. This is the closest I have got to working.
var imagesArray = [
"1.jpg", "2.jpg", "3.png", "4.png",
"5.png", "6.jpg", "7.jpg", "8.png"
];
function displayImage() {
var rand = imagesArray[Math.floor(Math.random() * 8)
document.canvas.src = imagesArray[rand];
}
With this code, I am getting an "Unexpected token: document" error. I don't even know if the other parts work, they just aren't erroring.
Where am I going wrong?
This code satisfies the requirement of cyclic uniqueness.
var imagesArray = [...]; // initial array
var imagesArrayTemp = imagesArray.slice(0); // clone of initial array
function displayImage() {
var index = Math.floor(Math.random() * imagesArrayTemp.length);
document.canvas.src = imagesArrayTemp[index];
imagesArrayTemp.splice(index, 1); // remove just displayed item
if(!imagesArrayTemp.length) {
console.log('All images have been displayed');
imagesArrayTemp = imagesArray.slice(0); // clone again, let's do a cycle
}
}
Related
I have a product page that recently had a product add-on tool made for it. I am modifying the code to change it's format a bit. There is a "add to cart" button bar that displays the total price before adding to cart.
I used .innerHTML to match the price of the top of the page for consistency, but I am trying to include the price of the product addons once selected.
Current code for bottom bar:
var selector1 = ".top-bar";
var el1 = document.querySelector(selector1);
var selector2 = ".bottom-bar";
var el2 = document.querySelector(selector2);
var totalPriceSelected = '';
function getPrice() {
el2.innerHTML = el1.innerHTML;
};
document.addEventListener('variant:priceChange', ()=>{
getPrice();
});
The add-on tool is a vue app which I am a little new to. I managed to get to the point that I can get the addons to print the prices to the console once selected:
watch: {
selected: function(new_value) {
setTimeout(function(){
var priceSelected = document.querySelectorAll(".price_selected");
var strippedPriceSelected = priceSelected;
for(var i=0;i<strippedPriceSelected.length;i++){
var totalPriceSelected = priceSelected[i].innerHTML;
console.log(totalPriceSelected);
}
}, 10);
this.product.selected = new_value;
//this.$emit('selected', this.product, new_value);
}
Currently, once the add-on products are selected, the vue adds the class of ".price_selected" to the price element once it is selected. The code above looks for ".price_selected", saves it to the variable totalPriceSelected, then prints it to the console.
I am wondering how I can add all of the addon price values (once selected) to the total price in the add to cart bar. I'd imagine I'd have to convert all the prices to numbers instead of strings and add them together, but I'm not quite sure how to do that.
Product page for reference
UPDATE
watch: {
selected: function(new_value) {
setTimeout(function(){
var priceTotal = 0
var priceSelected = document.querySelectorAll(".price_selected");
var strippedPriceSelected = priceSelected;
for(var i = 0; i < strippedPriceSelected.length; i++){
var totalPriceSelected = priceSelected[i].innerHTML; // '+$ 85.00'
var priceNumber = parseFloat(totalPriceSelected.split(' ')[1]);
priceTotal += priceNumber;
}
var currentBottomPrice = parseFloat(el2.innerHTML);
el2.innerHTML = currentBottomPrice += parseFloat(priceTotal);
console.log(priceTotal);
}, 10);
this.product.selected = new_value;
//this.$emit('selected', this.product, new_value);
}
},
So I have this now which is adding them correctly, but when the value of priceTotal goes back to 0 (add-ons are deselected) the total stays the same because it added the value and it is tr
Yeah so you can split the price string into two parts on the space, then parse the second part as a float (decimal number). Now you can do any math you need to it.
setTimeout(function() {
var priceSelected = document.querySelectorAll(".price_selected");
var strippedPriceSelected = priceSelected;
for(var i = 0; i < strippedPriceSelected.length; i++){
var totalPriceSelected = priceSelected[i].innerHTML; // '+$ 85.00'
var priceNumber = parseFloat(totalPriceSelected.split(' ')[1]);
console.log(priceNumber) // Prints 85 as number
}
}, 10);
Let me know if you have any questions or it doesn't work, I'd be happy to help!
We shouldn't just seek to have a working piece of code that solves a minor issue but could pose a major functionality break later.
I see potential pitfalls around the approach used here:
a) converting from string to number (using Number/parseFloat) could pose issues if the string contains invalid characters. For e.g., if price is from the add-on is displayed as '+$85.00' (notice the missing space after $ symbol), totalPriceSelected.split(' ')[1] will return undefined. Adding undefined to a number will give you NaN. Or, what if the add-on starts displaying the price as '85.00 $'. The approach used above would fail again.
b) What if the vue component updates the CSS class for prices to something else, say amount_selected? Such a change would be beyond your control but has a direct impact on the code/logic that you would implement to display the total price. Needless to say, when it comes to dealing with showing/requesting prices from users, we should be extremely careful about it.
There could be more such cases and we should look for a solution that is more robust.
i want to pull data from a .csv file and pass this data to a function to format every row of the .csv file to be a single object. These objects are then stored in an array called "list". So far so good. This is all working.
Next i want a button that calls a function onclick called "roll" that takes a random index from the "list" array and saves an instance of the random oject in a temporarily variable called randomItem.
Then i want to check certain properties of randomItem for specific values and if a certain condition is met it should change a specific property called "randomItem.Name". Finally i want the altered "randomItem" to be pushed into a new array called "results". These results are then being displayed on the website.
If I change the propertys value with "randomItem.Name = randomItem.Name + 'someString'" it also overwrites the original object in the "list" array. I dont want it to do this as i want to repeat the process of rolling random objects from this list several times. Therefor i need the "list" array to keep its original state. I cant get my head around why it overwrites the any list.
html
<button id="btnItem1">Roll</button>
js
$(document).ready(function() {
let list = [];
let results = [];
$('#btnItem1').click({slotName:'item1', listName:list, index:0}, roll);
// i need to pass slotName, listName and index because i have several buttons and list in the real project
getData('data.csv').then(data => formatData(data)); // get data from csv files
async function getData(url) {
const response = await fetch(url);
const data = await response.text();
return data;
};
function formatData(data) { // real formatting function taken out for stackflow
let formatted = // some formatting stuff;
list.push(formatted);
};
function roll(options) {
const slot = options.data.slotName;
const listTemp = options.data.listName;
const index = options.data.index;
if (slot.includes('item')) { // real criteria taken out for stackflow
do {
const randomNumber = Math.floor(Math.random() * (listTemp.length - 1) + 1);
let randomItem = listTemp[randomNumber];
if (1 == 1) { // real criteria taken out for stackflow
let chance = Math.round(Math.random());
if (chance) {
randomItem.Name += '(altered)';
}
}
results.splice(index, 1, randomItem);
} while (1 != 1); // real criteria taken out for stackflow
};
};
});
I expect it to write the altered "randomItem.Name" only to randomItem itself. Not even to listTemp but definitly not to the global "list". Do you have any idea why it is doing this and how i can prevent this? How can i get the object into randomItem without randomItem keeping its reference to any list. Thank you guys in advance!
I am trying to create a multiple choice question form to be created from data in a google spreadsheet. I managed to create the form of 60 questions each with 4 choices and setting the correct choice based on the information I have in the spreadsheet.
Last thing I need to do is to insert the correct feedback for each question based on column G in my spreadsheet that contains the feedback for each question.
Edit: here is a picture of how my spreadsheet & form would look like
Picture for Spreadsheet
Picture for how the form questions should look like
Picture of how the form questions look like (without a feedback)
The problem is that is not being implemented, The maximum I could was to set a fixed feedback/word for all questions, but was not possible to import the specific feedback for each question to the feedback section of each question, could anyone help with that, below is my code:
function popForm() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Sheet1');
var numberRows = sheet.getDataRange().getNumRows();
var myQuestions = sheet.getRange(1,1,numberRows,1).getValues();
var myAnswers = sheet.getRange(1,2,numberRows,1).getValues();
var myGuesses = sheet.getRange(1,2,numberRows,4).getValues();
var myfeedback = sheet.getRange(1,7,numberRows,1).getValues();
var myShuffled = myGuesses.map(shuffleEachRow);
Logger.log(myShuffled);
Logger.log(myAnswers);
// Create the form as a quiz. The resulting form's "Quiz options" are different from a manually created quiz. Be aware (and change manually if needed!
var form = FormApp.create('Fast Track Question - Domain I');
form.setIsQuiz(true);
// Write out each multiple choice question to the form.
for(var i=0;i<numberRows;i++){
if (myShuffled[i][0] == myAnswers[i][0]) {
var addItem = form.addMultipleChoiceItem();
addItem.setTitle(myQuestions[i][0])
.setPoints(1)
.setChoices([
addItem.createChoice(myShuffled[i][0],true),
addItem.createChoice(myShuffled[i][1]),
addItem.createChoice(myShuffled[i][2]),
addItem.createChoice(myShuffled[i][3])
]);
var incorrectFeedback = FormApp.createFeedback()
.setText(myfeedback[i][7])
.build();
addItem.setFeedbackForIncorrect(incorrectFeedback);
}
else if (myShuffled[i][1] == myAnswers[i][0]) {
var addItem = form.addMultipleChoiceItem();
addItem.setTitle(myQuestions[i][0])
.setPoints(1)
.setChoices([
addItem.createChoice(myShuffled[i][0]),
addItem.createChoice(myShuffled[i][1],true),
addItem.createChoice(myShuffled[i][2]),
addItem.createChoice(myShuffled[i][3])
]);
var incorrectFeedback = FormApp.createFeedback()
.setText(myfeedback[i][7])
.build();
addItem.setFeedbackForIncorrect(incorrectFeedback);
}
else if (myShuffled[i][2] == myAnswers[i][0]) {
var addItem = form.addMultipleChoiceItem();
addItem.setTitle(myQuestions[i][0])
.setPoints(1)
.setChoices([
addItem.createChoice(myShuffled[i][0]),
addItem.createChoice(myShuffled[i][1]),
addItem.createChoice(myShuffled[i][2],true),
addItem.createChoice(myShuffled[i][3])
]);
var incorrectFeedback = FormApp.createFeedback()
.setText(myfeedback[i][7])
.build();
addItem.setFeedbackForIncorrect(incorrectFeedback);
}
else if (myShuffled[i][3] == myAnswers[i][0]) {
var addItem = form.addMultipleChoiceItem();
addItem.setTitle(myQuestions[i][0])
.setPoints(1)
.setChoices([
addItem.createChoice(myShuffled[i][0]),
addItem.createChoice(myShuffled[i][1]),
addItem.createChoice(myShuffled[i][2]),
addItem.createChoice(myShuffled[i][3],true)
]);
var incorrectFeedback = FormApp.createFeedback()
.setText(myfeedback[i][7])
.build();
addItem.setFeedbackForIncorrect(incorrectFeedback);
}
}
}
// This function, called by popForm, shuffles the 5 choices.
function shuffleEachRow(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
Proposed change to script
Your code was long and I found it easier to re-write it with a few extra tools such as getDataRange, push and splice and forEach.
It seemed you were calling the methods in the right way, but since you were having to repeat yourself in a few places and keep track of many arrays and indices, it is likely that a small mistake came up.
This is a working script adapted from yours:
function createQuiz() {
let file = SpreadsheetApp.getActive();
let sheet = file.getSheetByName("Sheet1");
// Instead of getting individual ranges, it is more efficient
// to get all the data in one go, and then operate on the two
// dimensional array in memory.
let range = sheet.getDataRange();
let values = range.getValues();
// Here I am using a existing form to test, but you can just
// create a new one if you want.
var form = FormApp.openById("[TESTING_ID]");
form.setIsQuiz(true);
values.shift(); // Using this to remove the first row of headers
// Going through each line using a forEach to create a
// multiple choice question
values.forEach(q => {
let choices = [q[1], q[2], q[3], q[4]];
let title = q[0];
let feedback = q[5]
// Calling function to create multiple choice question
createShuffledChoices(form, title, choices, feedback)
});
}
function createShuffledChoices(form, title, choices, feedback){
let item = form.addMultipleChoiceItem();
item.setTitle(title)
.setPoints(1)
// Setting up the array that will be passed into item.setChoices()
let shuffledChoices = [];
// Making sure that the correct answer is only marked once
let correctAnswerChosen = false;
// I found I had to shuffle the questions within the process of
// creating choices as it made it easier to maintain the spreadsheet
for (let i = choices.length; i != 0; i--) {
let rand = Math.floor(Math.random() * (i - 1));
// If the first answer is chosen, it is the correct one.
if (rand == 0 && correctAnswerChosen == false) {
// Combination of push and splice to remove from ordered array
// to the shuffled one
shuffledChoices.push(item.createChoice(choices.splice(rand, 1)[0], true));
// Marking the correct answer as chosen,
// so that no others are marked correct.
correctAnswerChosen = true;
} else {
shuffledChoices.push(item.createChoice(choices.splice(rand, 1)[0]));
}
}
// Finally setting the choices.
item.setChoices(shuffledChoices);
// Creating the feedback
let formFeedback = FormApp.createFeedback().setText(feedback).build();
item.setFeedbackForIncorrect(formFeedback);
}
The way that you were creating feedback was correct, I suspect that you were just getting mixed up with your arrays and indexes. This is why I tried to simplify your code and eliminate repeated sections.
I combined the shuffling process with the creation of the multiple choice question. This is because the shuffled array that is passed into item.setChoices has to be built of item.createChoice objects. This can't be done in another scope because item is not available.
Combining the logic for shuffling this way means that you don't need to have the letter prefixes in your questions A). You also don't need the column that has the correct answer, because the process knows that the first answer is the correct one. So your sheet can be simplified to this:
For this script to work, the data needs to be organized in this way. (Though you can adapt it anyway you like of course)
References
getDataRange
push
splice
shift
forEach
I've got an array / object like below:
{
“Food’ : [{‘ImagesId”: ”38”, ”ImagesPath”: ”url”, ”Tag”: ”Food”}],
“Sport’: [{‘ImagesId”: ”64”, ”ImagesPath”: ”url”, ”Tag”: ”Sport”}]
}
I want to be able to display one image at a time and have buttons to go forward and back through the images. I need to have a variable that describes the currently viewed image.
Below is what I currently have and it allows me move forward through the array but not back. I just want a simple function that randomises the array above displays one image at a time and allowing the user to move forward and back through the images.
Finally I need something like window.currentlyShown
function receivedData(data) {
function changeOfferPicture() {
if (window.currentlyShownOffer) {
var tagArray = data[window.currentlyShownOffer.Tag];
tagArray.splice(tagArray.indexOf(currentlyShownOffer), 1);
//alert(window.currentlyShownOffer.ImagesId);
}
var tags = Object.keys(data);
var randomTag = tags[Math.floor(Math.random() * tags.length)];
var tagArray = data[randomTag];
window.currentlyShownOffer = tagArray[Math.floor(Math.random() * tagArray.length)];
window.shownCache.push(window.currentlyShownOffer);
document.getElementById('top5').src = window.currentlyShownOffer.ImagesPath;
};
}
I have a folder containing 10 images (img-1.jpg, img-2.jpg, ...). I currently feature 6 images on a site and I am trying to randomly swap out one of the 6 images with a different one that is not already shown on the page.
I have an array of all of my images (fullList), then I generate and array of the images that are currently shown (currentList).
Where I am having an issue is looping through the currentList array until I find a randomly generated item that is not currently in the currentList array. Then I will chose a random image from the page and change the image.
What I have now:
function sawpHeadshots() {
var fullList = [1,2,3,4,5,6,7,8,9,10];
var currentList = [];
$('#headshots li').each(function() {
currentList.push(parseInt($(this).css('background-image').replace(/\D+/, '')));
});
function generateRandom() {
return fullList[Math.floor(Math.random() * fullList.length)];
}
var rand = generateRandom();
/* not sure how to proceed at this point. */
}
Create a copy of the array randomly sort it, and remove them from the array when you create it. No need to keep generating random numbers or keeping track what was used.
var fullList = [1,2,3,4,5,6,7,8,9,10];
var random = fullList.slice(0).sort(function() {
return .5 - Math.random();
});
//get value with pop()
var current = random.pop();