I am currently making a node API where the user will be able to use my paint app. The user will be able to draw a picture and thereafter "add it to gallery" where it will land into my gallery.html where it will be displayed along with a description (title if you may) of the users painting. From there, the user will have the option to edit the description of their added painting and also delete it, if so desired. However i have thus far managed to make the image be sent to the gallery as a table of the Image, and description but not managed to get the edit and delete made to work yet. Any suggestions what code i could put in to address this issue with the style of code I have put in?
the code i am showing is solely the mechanics of where the crud operation is happening. the actual paint js, html and css is in other files.
best regards!
this is what my code looks like currently:
const loadImages = async () => {
const res = await fetch('/images')
if (res.ok){
console.log('ok')
const gallery = document.getElementById('gallery')
const data = await res.json()
//the creation of table upon adding to gallery
const table = document.createElement('table')
table.classList.add('galleryTable')
const tableHeader = document.createElement('tr')
table.classList.add('tableTR')
const tableHeaderImage = document.createElement('th')
tableHeaderImage.innerText = 'Paintings'
const tableHeaderDesc = document.createElement('th')
tableHeaderDesc.innerText = 'Description'
const tableHeaderControllers = document.createElement('th')
tableHeaderControllers.innerText = 'Options'
tableHeader.appendChild(tableHeaderImage)
tableHeader.appendChild(tableHeaderDesc)
tableHeader.appendChild(tableHeaderControllers)
table.appendChild(tableHeader)
//a foreach loop to continously adding and collecting the saves images into the gallery
data.forEach(image =>{
const tableRow = document.createElement('tr')
const tableDataImage = document.createElement('td')
const img = document.createElement('img')
img.src= image.imagedata
tableDataImage.appendChild(img)
tableRow.appendChild(tableDataImage)
const tableDataDesc = document.createElement('td')
tableDataDesc.innerText = image.description
tableRow.appendChild(tableDataDesc)
const tableDataControllers = document.createElement('td')
tableDataControllers.innerText = 'Options'
tableRow.appendChild(tableDataControllers)
table.appendChild(tableRow)
})
gallery.appendChild(table)
} else {
console.log(res)
}
}
loadImages()
Related
I am trying to figure out how to make a script to run a function based on an answer provided, at the moment my script creates and populates a word doc with answers provided from a google form. I want to streamline it so that I can use one google form to create different documents based on an answer provided rather than creating multiple google forms. I am very new to JavaScript and I am pretty sure I need an if statement at the beginning of everything, but I don't know what I should write or where it should go. This is my script:
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs(){
const googleDocTemplate = DriveApp.getFileById('google file goes here');
const destinationFolder = DriveApp.getFolderById('google folder goes here');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Response');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row,index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[5]} document name goes here`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
body.replaceText('{{Name}}', row[5]);
body.replaceText('{{To}}', row[6]);
body.replaceText('{{Items}}', row[7]);
body.replaceText('{{Reasoning}}', row[8])
body.replaceText('{{Submitter}}', row[1]);
body.replaceText('{{Role}}', row[2]);
body.replaceText('{{Time}}', row[3]);
body.replaceText('{{Discord}}', row[4]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}
I have a project which is responsible for managing the rendering of elements, but I'm running into a performance issue replacing elements and then focusing on whatever had focus before.
Below is a minimal example that replicates the performance issue:
const renderPage = () => {
// get the old section element
const oldSection = document.querySelector('section')
// create a new section element (we'll replaceWith later)
const newSection = document.createElement('section')
// create the render button
const newButton = document.createElement('button')
newButton.innerHTML = 'Render Page'
newButton.onclick = renderPage
newSection.appendChild(newButton)
// create a bunch of elements
const dummyDivs = [...new Array(100000)].forEach(() => {
const dummy = document.createElement('div')
dummy.innerHTML = 'dummy'
newSection.appendChild(dummy)
})
// replace the old page with the new one (causes forced reflow)
oldSection.replaceWith(newSection)
// reattach focus on the button (causes forced reflow)
newButton.focus()
}
window.renderPage = renderPage
<section>
<button onclick="renderPage()">Render</button>
</section>
When running this locally, I see the following in the performance report in Chrome/Edge
Both replaceWith and focus are triggering forced reflow. Is there a way to batch or group these actions so that only a single reflow occurs? I realize that there's no way to really get around this happening at all, but if I can batch them, I think that might improve my performance.
Indeed, focus always causes a reflow: What forces layout / reflow
So what you may do, is to reduce the reflowtime by inserting the new button standalone, initiate focus and after that you can append other childs:
Working example: Example
const renderPage = () => {
// get the old section element
const oldSection = document.querySelector('section')
// create a new section element (we'll replaceWith later)
const newSection = document.createElement('section')
// create the render button
const newButton = document.createElement('button')
newButton.innerHTML = 'Render Page'
newButton.onclick = renderPage
newSection.appendChild(newButton)
// create a bunch of elements
const dummies = []; // store in seperate array
const dummyDivs = [...new Array(100000)].forEach(() => {
const dummy = document.createElement('div')
dummy.innerHTML = 'dummy';
dummies.push(dummy)
})
// insert new section only with new button
oldSection.replaceWith(newSection)
newButton.focus(); // always causes reflow; but fast because it's only one element
// store all other nodes after focus
newSection.append(...dummies)
}
window.renderPage = renderPage
I am trying to create an app that, with info given by the user, gets a PDF formulary from local assets, writes that info on it and then downloads it.
It works perfectly fine on browser and I get the file sucessfully, but when I create the app build and I try it in the movile device, just nothing happens. I get messages of "File downloaded!" or any log I put, but the file just never start downloading. I already have storage permissions. Just ends the function normally but ignoring the download.
I use the unpkg library to download, but also tried with SaveAs and creating an <a.href> then click it but I got excactly the same result.
This is the full function:
async generateDecklist()
{
const formUrl=('../../../assets/decklist.pdf');
const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(formPdfBytes);
const form = pdfDoc.getForm();
const formFields = form.getFields()
//START FILLING FORM
//Deck Name & Nation
form.getTextField('Deck NameRow1').setText(this.deck.name);
form.getTextField('ClanRow1').setText(this.deck.nation);
for (let i = 0; i<this.deck.decklist.length; i++)
{
const cs = this.deck.decklist[i];
const card = Global.cards.find(e => e.id == cs.cardId);
//Card Name
const mainDeckCard = "Main deck 50 cardsRow" + (i+1);
form.getTextField(mainDeckCard).setText(card.name);
//Card Grade
formFields[16+i].setText(card.grade.toString());
//Card Amount
const mainDeckAmount = "Main deck Qty" + (i+1);
form.getTextField(mainDeckAmount).setText(cs.amount.toString());
//Card set (Only if there is only one set)
if(card.sets.length ==1)
{
const mainDeckSet = 'No.'+ ((i+13)>=15? (i+14) : (i+13));
form.getTextField(mainDeckSet).setText(card.sets[0]);
}
//Card trigger or sentinel
let triggerOrSentinel ='';
if(card.type == 'Trigger Unit')
{
triggerOrSentinel += card.trigger;
}
if(card.keywords.includes('Sentinel'))
{
triggerOrSentinel += triggerOrSentinel==''? 'Sentinel' : '/Sentinel';
}
const mainDecktrigger = "Main deck Row" + (i+1);
form.getTextField(mainDecktrigger).setText(triggerOrSentinel);
}
//END FILLING FORM
//PROBLEM STARTS HERE
const pdfBytes = await pdfDoc.save();
download(pdfBytes, `${this.deck.name}_decklist.pdf`, "application/pdf");
modalController.dismiss();
}
I am trying to scrape this page.
https://www.psacard.com/Pop/GetItemTable?headingID=172510&categoryID=20019&isPSADNA=false&pf=0&_=1583525404214
I want to be able to find the grade count for PSA 9 and 10. If we look at the HTML of the page, you will notice that PSA does a very bad job (IMO) at displaying the data. Every TR is a player. And the first TD is a card number. Let's just say I want to get Card Number 1 which in this case is Kevin Garnett.
There are a total of four cards, so those are the only four cards I want to display.
Here is the code I have.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://www.psacard.com/Pop/GetItemTable?headingID=172510&categoryID=20019&isPSADNA=false&pf=0&_=1583525404214");
const tr = await page.evaluate(() => {
const tds = Array.from(document.querySelectorAll('table tr'))
return tds.map(td => td.innerHTML)
});
const getName = tr.map(name => {
//const thename = Array.from(name.querySelectorAll('td.card-num'))
console.log("\n\n"+name+"\n\n");
})
await browser.close();
})();
I will get each TR printed, but I can't seem to dive into those TRs. You can see I have a line commented out, I tried to do this but get an error. As of right now, I am not getting it by the player dynamically... The easiest way I would think is to create a function that would think about getting the specific card would be doing something where the select the TR -> TD.card-num == 1 for Kevin.
Any help with this would be amazing.
Thanks
Short answer: You can just copy and paste that into Excel and it pastes perfectly.
Long answer: If I'm understanding this correctly, you'll need to map over all of the td elements and then, within each td, map each tr. I use cheerio as a helper. To complete it with puppeteer just do: html = await page.content() and then pass html into the cleaner I've written below:
const cheerio = require("cheerio")
const fs = require("fs");
const test = (html) => {
// const data = fs.readFileSync("./test.html");
// const html = data.toString();
const $ = cheerio.load(html);
const array = $("tr").map((index, element)=> {
const card_num = $(element).find(".card-num").text().trim()
const player = $(element).find("strong").text()
const mini_array = $(element).find("td").map((ind, elem)=> {
const hello = $(elem).find("span").text().trim()
return hello
})
return {
card_num,
player,
column_nine: mini_array[13],
column_ten: mini_array[14],
total:mini_array[15]
}
})
console.log(array[2])
}
test()
The code above will output the following:
{
card_num: '1',
player: 'Kevin Garnett',
column_nine: '1-0',
column_ten: '0--',
total: '100'
}
Intended Goal - User selects different colors from various color inputs and creates their own theme. Once the colors are chosen, the user clicks the download button and gets the generated CSS file with the colors he/she chose.
Issue - I'm able to download the CSS file, but I'm getting the original values despite changing the inputs to different colors.
What I've Done
The CSS file that's being downloaded already exists and all of the colors that correspond to different elements are done via CSS variables.
I'm updating the changes live by doing the following.
import { colors } from './colorHelper'
const inputs = [].slice.call(document.querySelectorAll('input[type="color"]'));
const handleThemeUpdate = (colors) => {
const root = document.querySelector(':root');
const keys = Object.keys(colors);
keys.forEach(key => {
root.style.setProperty(key, colors[key]);
});
}
inputs.forEach((input) => {
input.addEventListener('change', (e) => {
e.preventDefault();
const cssPropName = `--${e.target.id}`;
document.styleSheets[2].cssRules[3].style.setProperty(cssPropName, e.target.value);
handleThemeUpdate({
[cssPropName]: e.target.value
});
console.log(`${cssPropName} is now ${e.target.value}`)
});
});
Then, I fetched the stylesheet from the server, grabbed all the CSS Variables and replaced them with their actual value (hex color value).
After that, I got the return value (new stylesheet without variables) and set it for the data URI.
async function updatedStylesheet() {
const res = await fetch("./prism.css");
const orig_css = await res.text();
let updated_css = orig_css;
const regexp = /(?:var\(--)[a-zA-z\-]*(?:\))/g;
let cssVars = orig_css.matchAll(regexp);
cssVars = Array.from(cssVars).flat();
for (const v of cssVars) {
updated_css = updated_css.replace(v, colors[v.slice(6, -1)]);
};
return updated_css;
}
const newStylesheet = updatedStylesheet().then(css => {
downloadBtn.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(css));
downloadBtn.setAttribute('download', 'prism-theme.css');
})
I already have a download button setup in my HTML and I grabbed it earlier in my script so that it was available anywhere for me to use. downloadBtn
I set up the button to fire and grabbed the new sheet.
downloadBtn.addEventListener('click', () => {
newStylesheet();
});
The Result
I get the initial color values of the stylesheet despite changing the colors within the color inputs on the page. So the CSS file isn't being updated with the new colors before I download the file.
You could use PHP to pass the values to a new page. Let's say you chose the colors you want then click a button that says "Generate" that takes you to the "Generate Page".
The values would be passed directly into the HTML and you would download from the Generate Page instead.
This is if you know PHP of course, just a suggestion on how you might solve it.
(would comment, but can't due to reputation)