Add numbers from dynamic site with new page load - javascript

How do you write a function/ listener in javascript that can fire when html updates?
html page is limited to the last 100 records
html page continuously adds new records (nodes)
I need to...
push the values into an array or increment a variable to sum the values
display the running total in html
Trying to create a counter that adds new values to the sum.
I believe the code below only executes once.
window.onload = function() {
var data = document.getElementsByTagName('span');
var len = data.length;
var total = 0;
var searchValue = "value";
for (var i = 0; i < len; ++i) {
var styles = data[i].getAttribute('style');
if (styles == searchValue) {
var txt = data[i].innerHTML;
split = txt.split(" ");
total += parseInt(split[2]);
}
}
console.log(total);
};

Yes you can do this with just JavaScript, and a little HTML/CSS for displaying the number.
Note that it requires a fair deal of experience to manage an addon with cross-origin requests, and to show it to the user.

Related

Count the number of cells that have the text strikethrough

I would like to create a custom function to count the number of strikethrough text. Would it be possible to create a script to perform this operation?
For the moment, I managed to create a script that counts the cell number with the strikethrough text, but I have to modify my script each time for a different range, and I can not create a custom function that count the number of cells that have the text strikethrough.
Here is my script:
The result is in the L20 and the control in the script
// Count the number of cells that are barred
function fontLine() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var plage = "E4:E11"
var range = sheet.getRange(plage);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
if (cell.getFontLine() === 'line-through')
x++;
}
}
sheet.getRange("L20").setValue(x);
return x;
}
It's not possible to use setValue in a custom function but, as you already figured out, it's possible to get the result you are looking for by using a "regular script" (running it from a menu, by clicking an image, from the script editor, etc.)
Reference
https://developers.google.com/apps-script/guides/sheets/functions
Modifications of your code
It now uses getActiveSheet() to obtain the sheet from where the function is called, and getDataRange() to obtain the whole range in the sheet where there is data.
It uses getFontLines() on the whole range instead of iterating cell-per-cell and calling getFontLine() to every single one of them. This results in a way more efficient code (see: Best Practices - Minimize calls to other services).
Removed setValue() call. If you want the result of this custom function to be placed in the L20 cell, simply modify its contents for this formula: =fontLine(). The value returned by the custom function will be set as the cell's value.
// Count the number of cells in the sheet that are barred
function fontLine() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var fontLines = range.getFontLines();
var x = 0;
for (var i = 0; i < fontLines.length; i++) {
for (var j = 0; j < fontLines[i].length; j++) {
if (fontLines[i][j] === 'line-through')
x++;
}
}
return x;
}
Example execution
Disclaimers
Custom functions are executed upon writing them into a cell. Afterwards, their result is cached so that less resources are used. That means that if you add or remove strike-through's from your sheet, you will have to remove and re-insert the custom function in your cell.

createElement creates infinite loop

I'm really new to javascript, and coding in general, and I can't understand why this causes an infinite loop:
let newTr = document.createElement('tr');
If I take it out, the webpage loads fine, but if I leave it in, the webpage never fully loads and my browser uses 50% of my CPU.
Here's the rest of my code:
// client-side js
// run by the browser each time your view template referencing it is loaded
console.log('hello world :o');
let arrPfcCases = [];
// define variables that reference elements on our page
const tablePfcCases = document.getElementById("tablePfcCases");
const formNewPfcCase = document.forms[0];
const caseTitle = formNewPfcCase.elements['caseTitle'];
const caseMOI = formNewPfcCase.elements['caseMOI'];
const caseInjuries = formNewPfcCase.elements['caseInjuries'];
// a helper function to call when our request for case is done
const getPfcCaseListener = function() {
// parse our response to convert to JSON
arrPfcCases = JSON.parse(this.responseText);
// iterate through every case and add it to our page
for (var i = 0; i = arrPfcCases.length-1;i++) {
appendNewCase(arrPfcCases[i]);
};
}
// request the dreams from our app's sqlite database
const pfcCaseRequest = new XMLHttpRequest();
pfcCaseRequest.onload = getPfcCaseListener;
pfcCaseRequest.open('get', '/getDreams');
pfcCaseRequest.send();
// a helper function that creates a list item for a given dream
const appendNewCase = function(pfcCase) {
if (pfcCase != null) {
tablePfcCases.insertRow();
let newTr = document.createElement('tr');
for (var i = 0; i = pfcCase.length - 1; i++) {
let newTd = document.createElement('td');
let newText = document.createTextNode(i.value);
console.log(i.value);
newTd.appendChild(newText);
newTr.appendChild(newTd);
}
tablePfcCases.appendChild(newTr);
}
}
// listen for the form to be submitted and add a new dream when it is
formNewPfcCase.onsubmit = function(event) {
// stop our form submission from refreshing the page
event.preventDefault();
let newPfcCase = [caseTitle, caseMOI, caseInjuries];
// get dream value and add it to the list
arrPfcCases.push(newPfcCase);
appendNewCase(newPfcCase);
// reset form
formNewPfcCase.reset;
};
Thanks!
P.S. There are probably a ton of other things wrong with the code, I just can't do anything else until I figure this out!
As an explanation, in your code
i = pfcCase.length - 1
assigned the value of pfcCase.length - 1 to i. The syntax of that part of the loop should be
an expression to be evaluated before each loop iteration. If this expression evaluates to true, statement is executed.
The evaluation of your code made no sense.
Evaluating
i < pfCase.length
before each iteration to check that the current index is less than the length of the array, however, works correctly.
Here is no conditional statement here. In this statement you are assigning pfcCase length minus 1 to the I variable.
for (var i = 0; i = pfcCase.length - 1; i++) {
You have to compare the i variable to the length of the pfcCase minus 1.
This should work.
for (var i = 0; i < pfcCase.length - 1; i++) {
noticed something else
This line does not do what you think it dose.
let newText = document.createTextNode(i.value);
i is just the index i.e. a number. It does not have the value property.
This is what you are looking to do.
let newText = document.createTextNode(pfcCase[i].value);
my preference (forEach)
I prefer using the array forEach method. It’s cleaner and less prone to mistakes.
pfcCase.forEach( function(val){
let newTd = document.createElement('td');
let newText = document.createTextNode(val.value);
console.log('The array element is. '. val.value, ' The value is. ', val.value);
newTd.appendChild(newText);
newTr.appendChild(newTd);
});

Trying to do essentially a vlookup in an AdWords Script but it takes too long

I'm trying to match the location input (which may be a post code or a neighborhood) to the AdWords location ID. I have a massive google sheet with almost 100,000 rows with a list of all the post codes, cities, neighborhoods, etc. and their corresponding IDs, and I'm trying to write a function where you can input post code (for example) and it gives you the location ID. So far I have:
var locationSpread = SpreadsheetApp.openById('scriptID');
var locationSheet = locationSpread.getSheetByName('Sheet1');
var locationRow = locationSheet.getLastRow();
var locationMatch = function(locationInput) {
for (var i = 2; i <= locationRow; i++) {
var locationName = locationSheet.getRange([i], 2).getValue();
if (locationName == locationInput) {
return locationSheet.getRange([i], 1);
}
}
}
Logger.log(locationMatch('DD7'));
I think that in theory this should work, but the limit for AdWords scripts is 30 minutes and since this has to iterate through so many rows it simply isn't feasible, it's taking over 20 minutes to return the example I'm logging, and the script should be doing this dynamically for a bunch of inputs.
Is there a faster/better way of doing this? Or is there an issue with the script itself that's causing it to run so slowly?
Using .getValues() might be faster. You'd need to try it out.
var locationSpread = SpreadsheetApp.openById('asdf');
var locationSheet = locationSpread.getSheetByName('asdf');
var locationRow = locationSheet.getLastRow();
Logger.log("Starting!")
Logger.log("LocationRow: "+locationRow);
function locationMatch(locationInput) {
var locationNames = locationSheet.getRange(1, 2,locationRow,2).getValues();
for (var i = 1; i <= locationRow-1; i++) {
Logger.log("locationNames[i][0]:"+locationNames[i][0])
if (locationNames[i][0] == locationInput) {
return locationSheet.getRange([i], 1);
}
}
}
Logger.log("locationMatch Result:"+locationMatch('MA'));

Use Google Apps Script functions in project

I am very new to Google Apps Scripts and am curious how I can use functions created in my own project. For example, I have a script bound to a spreadsheet with just one function:
function addOrder(title, content) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow([ Date(), title, content]);
}
It simply takes 2 arguments and adds a row to the spreadsheet with that data. I have deployed it as a web app, but I'm not sure how to use this function in an environment like JSFiddle. Any help is appreciated.
Thanks
Spreadsheet bound scripts run server side and the SpreadsheetApp.getActiveSheet() method you are using will only work in the context of a spreadsheet bound script since it's the only case where the script actually "sees" an active spreadsheet. When you deploy this as a webapp you will have to tell the script which spreadsheet it must look at using for example the SpreadsheetApp.openById('spreadsheet ID') method.
But even doing so will not allow for using such a code outside of Google environment (as in JS fiddle for example) since SpreadsheetApp is specific to Google Apps service.
You have to remember that Google Apps Script is based on JavaScript but is not "plain" JavaScript , it uses a lot of specific services that work only in relation with Google Apps.
edit to answer your comment below :
the code used in the spreadsheet to work as a data server goes like this : (this is deployed as a webapp without user interface. It runs as a service
function doGet(e) {
if(e.parameter.mode==null){return ContentService.createTextOutput("error, wrong request").setMimeType(ContentService.MimeType.TEXT)};
var mode = e.parameter.mode;
var value = e.parameter.value;
var ss = SpreadsheetApp.openById('1yad5sZZt-X6bIftpR--OSyf3VZWf3Jxx8UJBhh7Arwg');
var sh = ss.getSheets()[0];
if(mode=='read'){
var sheetValues = sh.getDataRange().getValues();// get data from sheet
var valToReturn = ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);
return valToReturn;// send it as JSon string
}
if(mode=='write'){
var val = Utilities.base64Decode(value,Utilities.Charset.UTF_8);// decode base64 and get an array of numbers
Logger.log(val);// see it !
var stringVal = ''; // create an empty string
for(var n in val){
stringVal += String.fromCharCode(val[n]);// add each character in turn
}
var sheetValues = JSON.parse(stringVal);// convert the string into an object (2D array)
Logger.log(sheetValues);// check result
sh.getRange(1,1,sheetValues.length,sheetValues[0].length).setValues(sheetValues);// update the sheet
return ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);// send back the result as a string
}
return ContentService.createTextOutput('error').setMimeType(ContentService.MimeType.TEXT);// in case mode is not 'read' nor 'write'... should not happen !
}
you can call this service by its url + parameters and it will get / set values in the spreadsheet. This is a basic example but it works nicely.
below it the webapp code of the Ui that uses this service in this spreadsheet
var stylePanel = {'padding':'50px', 'background':'#FFA'};
var styleButton = {'padding':'5px', 'border-radius':'5px', 'borderWidth':'1px', 'borderColor':'#DDD','fontSize':'12pt'};
var styleTextItalic = {'fontSize':'12pt','fontStyle':'italic','fontFamily':'arial,sans-serif','color':'#F00'};
var styleTextNormal = {'fontSize':'12pt','fontStyle':'normal','fontFamily':'arial,sans-serif','color':'#00F'};
var styleLabel = {'fontSize':'12pt','color':'#F00'};
var url = 'https://script.google.com/macros/s/AKfycbwPioVjYMSrmhKnJOaF2GG83dnstLWI7isU9SF1vxPV8td-g9E7/exec';
var numRow = 21;// the number of rows in the grid = number of rows in the SS + 1
;
function doGet() {
var app = UiApp.createApplication().setTitle('url_fetch_demo');
var panel = app.createVerticalPanel().setStyleAttributes(stylePanel);
var headers = ['Field Name','Your answer'];// grid title
var grid = app.createGrid(numRow+2,2);// create the grid with right size
var wait = app.createImage('https://dl.dropboxusercontent.com/u/211279/loading3T.gif').setId('wait').setVisible(false);// get a spinner image in animated gif
var handlerWrite = app.createServerHandler('writeSheet').addCallbackElement(grid);// 2 handlers for the buttons
var handlerRead = app.createServerHandler('readSheet').addCallbackElement(grid);
var Chandler = app.createClientHandler().forTargets(wait).setVisible(true);// a client handler for the spinner
var buttonWrite = app.createButton('Write to Sheet',handlerWrite).addClickHandler(Chandler).setStyleAttributes(styleButton);
var buttonRead = app.createButton('Read from Sheet',handlerRead).addClickHandler(Chandler).setStyleAttributes(styleButton);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){ // create all the textBoxes with names & IDs
var textBox = app.createTextBox().setText('no value').setName('text'+n+'-'+m).setId('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
//if(m==0){textBox.setEnabled(false)};// prevent writing to left column (optional)
grid.setWidget(n,m,textBox);// place widgets
}
}
grid.setWidget(numRow,0,buttonRead).setWidget(numRow,1,buttonWrite).setWidget(numRow+1,1,wait) // place buttons
.setWidget(0,0,app.createLabel(headers[0]).setStyleAttributes(styleLabel)) // and headers
.setWidget(0,1,app.createLabel(headers[1]).setStyleAttributes(styleLabel));
app.add(panel.add(grid));
return app; // show Ui
}
function writeSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);// spinner will be hidden when fct returns
var dataArrayImage = [];// an array to get typed values
for(var n=1 ; n < numRow ; n++){
var row=[];
for(var m=0 ; m < 2 ; m++){
row.push(e.parameter['text'+n+'-'+m]); // get every value in every "cell"
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextItalic);// update "cells" style
//textBox.setText('written value = '+e.parameter['text'+n+'-'+m]);// rewrite to the cells - not usefull but serves to check while debugging
}
dataArrayImage.push(row);// store one row(=2cells)
}
var UiValues = JSON.stringify(dataArrayImage);// stringfy the array
var newValues = url+'?mode=write&value='+Utilities.base64Encode(UiValues,Utilities.Charset.UTF_8);// add to url & parameters+ encode in pure ASCII characters
Logger.log(newValues);// check in logger
var check = UrlFetchApp.fetch(newValues).getContent();// get back the result
Logger.log(check);// check result = newValues sent back in bytes format
return app;//update Ui
}
function readSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);
var returnedValue = UrlFetchApp.fetch(url+'?mode=read').getContentText();// get data from server
Logger.log(returnedValue);// check values
var sheetValues = JSON.parse(returnedValue);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
textBox.setText(sheetValues[n-1][m]);// iterate and update cells values
}
}
return app;// update Ui
}

Data copying with Google Apps Script

I am trying to write a script in Google Apps Script that takes cell information from one sheet and copies it to another sheet, both for just grabbing certain columns to display on the second sheet and also a condition based on the values inside cells in a certain column. Here is what I have so far:
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("xxxxx");
var masterSheet = myMaster.setActiveSheet(myMaster.getSheets()[0]);
var myNames = SpreadsheetApp.openById("xxxxx");
var namesSheet = myNames.setActiveSheet(myNames.getSheets()[0]);
var row1 = masterSheet.getRange(1, 1, masterSheet.getLastRow(), 1);
var rowV = row1.getValues();
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for (var i = 1; i < rowV.length; i++) {
dataList.push(firstArray[i][0]);
dataList.push(firstArray[i][1]);
dataList.push(firstArray[i][2]);
dataList.push(firstArray[i][3]);
}
for (var j = 0; j < rowV.length - 1; j++) {
namesSheet.getRange(2, j + 1, 1, 1).setValue(dataList[j]);
}
}
So as of now it only works on one row, starting from the second row (to allow for column headers). And I suppose when I want to grab rows conditionally based on cell data, I will use an 'if' statement for the condition inside the 'for' loop, but I want the data to copy to the next available row in both sheets. I suppose I'd use something like:
' getLastRow + 1 '
or something like that. I need this code to be as efficient as possible because of the amount of data and its purpose. I am pretty new to programming so please explain in detail, and thanks again.
I'm not sure I understood exactly what you wanted to do but -from what I understood- this code snippet should give you a better way to start with...
(I added a few comments to explain in the code itself)
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("MasterSheet ID");
var masterSheet = myMaster.getSheets()[0]; // get 1rst sheet
var myNames = SpreadsheetApp.openById("NamesSheet ID");
var namesSheet = myNames.getSheets()[0]; // get 1rst sheet
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for ( r = 1; r < firstArray.length; r++) { // iterate the first col of masterSheet
if(firstArray[r][0]=='some condition'){ // if value in the first column == 'some condition get the second column cell in the new array (here you could change what you want to get)
dataList.push([firstArray[r][1]])
}
}
Logger.log(dataList)
if(dataList.length>0){
namesSheet.getRange(1,namesSheet.getLastColumn()+1,dataList.length,1).setValues(dataList);//copy data in a column after last col
}
}

Categories

Resources