Im starting to do some small functions and tweaks on websites with javascript, but whats really bothers me is that I dont know how to run the javascript again after a function has run?
For instance if I call a function onclick which adds a user to an array that is shown in my website, the new user wont be displayed until the page is refreshed?
How do I work around this?
EXAMPLE:
if (!localStorage.myStorage) {
// CREATE LOCALSTORAGE
}else{
myArray = JSON.parse(localStorage.myStorage);
for (var i = 0; i < myArray.length; i++) {
if(myArray[i].id === 1){
$(".firstIdContainer").append("<p>" + myArray[i].userName + "</p>");
}
if(aUserLogin[i].id === 2) {
$(".secondIdContainer").append("<p>" + myArray[i].userName + "</p>");
}
}
}
$(document).on("click", ".btnRegisterUser", function() {
// ADD NEW USER TO LOCALSTORAGE
}
How do i make sure my new user i register will be shown immediately through my for loop displaying users.
Like:
if(!localStorage.myStorage){
// CREATE LOCALSTORAGE
}
function doIt(){
var myArray = JSON.parse(localStorage.myStorage);
for(var i in myArray){
var apd = '<p>' + myArray[i].userName + '</p>';
if(myArray[i].id === 1){
$(".firstIdContainer").append(apd);
}
else if(aUserLogin[i].id === 2) {
$(".secondIdContainer").append(apd);
}
}
}
}
doIt();
$('.btnRegisterUser').click(doIt);
Try creating a contentUpdate function that resets whatever is getting displayed and creates it again based on new variables (this would go at the bottom of a function to add the user, for example). The reason that variable changes aren't reflected in the DOM is that the DOM has no abstraction for how it was made; it's output, and it won't change itself based on what its input has done after it was put in.
If you just want to insert a new row into a table you don't need to refresh the page.
jsfiddle
html:
<table id="usertable">
<tr><td>user 1</td></tr>
</table>
<input id="newuser"></input>
<input id="adduser" type="submit"></input>
js:
var button = document.getElementById('adduser');
button.onclick = function(event) {
var user = document.getElementById('newuser').value
//add the user to your array here
//add a table row
var table = document.getElementById('usertable');
var row = table.insertRow(0);
var cell1 = row.insertCell(0);
cell1.innerHTML = user;
event.preventDefault();
}
Related
I've created a website using google app script. In my website, I have a table but the values came from my spreadsheet.
Now, I have an editable table unfortunately, once I change the values in the row and the auto refresh triggered it will come back to normal and get the original values from the spreadsheet. Is there a way that once I've made some changes it will also change in the spreadsheet so when autorefresh triggered it will just be the same? Is it even possible?
<script>
document.addEventListener("DOMContentLoaded",function(){
google.script.run.withSuccessHandler(generateTable).getOnline();
google.script.run.withSuccessHandler(generateTable1).getStatus();
setInterval(() => {
document.getElementById("tablebody").innerHTML = "";
document.getElementById("tablebody1").innerHTML = "";
google.script.run.withSuccessHandler(generateTable).getOnline();
google.script.run.withSuccessHandler(generateTable1).getStatus();
google.script.run.withSuccessHandler(getOnline).generateTable();
google.script.run.withSuccessHandler(getStatus).generateTable1();
}, 20000); // run the function every 5 seconds
});
function generateTable(dataArray){
var tbody = document.getElementById("tablebody");
var tbody1 = document.getElementById("tablebody").innerHTML;
dataArray.forEach(function(r){
var row = document.createElement("tr");
var col1 = document.createElement("td");
col1.textContent = r[0];
var col2 = document.createElement("td");
col2.textContent = r[1];
var col3 = document.createElement("td");
col3.textContent = r[2];
row.appendChild(col1);
row.appendChild(col2);
row.appendChild(col3);
tbody.appendChild(row);
$(function(){
$("td").click(function(event){
if($(this).children("input").length > 0)
return false;
var tdObj = $(this);
var preText = tdObj.html();
var inputObj = $("<input type='text' />");
tdObj.html("");
inputObj.width(tdObj.width())
.height(tdObj.height())
.css({border:"0px",fontSize:"17px"})
.val(preText)
.appendTo(tdObj)
.trigger("focus")
.trigger("select");
inputObj.keyup(function(event){
if(13 == event.which) { // press ENTER-key
var text = $(this).val();
tdObj.html(text);
}
else if(27 == event.which) { // press ESC-key
tdObj.html(preText);
}
});
inputObj.click(function(){
return false;
});
});
});
});
}
</script>
<table>
<tr>
<th>Timestamp & Current TimeZone</th>
<th>Name</th>
<th>EID</th>
</tr>
<tbody id="tablebody">
</table>
Here's my code on .gs
function getOnline(){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("UserLogins");
var data = ws.getRange(3, 1, ws.getLastRow()-1,3).getValues();
Logger.log(data);
return data;
}
I’m not sure of your use case, but it’s possible to embed an editable spreadsheet (see how here).
If you really want to do this with HTML + JavaScript for yourself, my approach would be:
Keep a version of what you know is in the backend. And another of the local changes, in different data structures.
Use functional paradigms to generate the table. Specially reactive frameworks (React, Vue.js, etc) will simplify your job a lot.
When “applying” changes, do not manually modify the backend data representation.
It should be kept as the data that the server sends you. Simply send the request and it will be updated automatically.
When the server sends a change that you had locally, remove it from the local changes. This consolidates the changes.
Also, you need to decide what happens when a cell gets updated while you are editing it.
Here is an example with vanilla JS on a single text input which can give you an idea (a lot of this could be handled by a reactive framework):
const elements = {
input: null,
stateView: null
}
const backend = {
input: 'value'
}
const local = {
}
function update() {
// Update backend with the backend information. Usually means a fetch
/* backend = ... */
if (local.input === backend.input) {
delete local.input
}
render()
}
function save() {
// Send a request updating the backend
// simulation
backend.input = local.input
update()
}
function localChange() {
local.input = elements.input.value
if (local.input === backend.input) {
delete local.input
}
render()
}
function render() {
input.value = 'input' in local ? local.input : backend.input
let state = ''
if ('input' in local) {
state += `Local: "${local.input}"<br/>`
}
state += `Backend: "${backend.input}"<br/>`
elements.stateView.innerHTML = state
}
// Setup events
document.addEventListener('DOMContentLoaded', function() {
elements.input = document.getElementById('input')
elements.input.addEventListener('keyup', localChange)
elements.input.addEventListener('change', save)
elements.stateView = document.getElementById('stateView')
update()
})
<input id="input" type="text" placeholder="Add you data here">
<p id="stateView"></p>
Notice that in this case if the user is editing and it’s the same I just ignore the local state. Also, because I’m not really making requests, everything is instantaneous.
I have this problem here
The problem has been solved, but my question is how can I get the second value from that, or the third one. The sheet will have many tables and at some point I will need a total for each table. Also, is there any solution to automatically find the the array number which contain date row for each table (instead defining this manually). Hope my explanation make sense.
Thank you!
Kind regards,
L.E. Test file
If I understood your question correctly, instead of breaking the loop when a match to "Total" is found do whatever is needed to be done within the loop like so...
var today = toDateFormat(new Date());
var todaysColumn =
values[5].map(toDateFormat).map(Number).indexOf(+today);
var emailDate = Utilities.formatDate(new Date(today),"GMT+1",
"dd/MM/yyyy");
for (var i=0; i<values.length; i++){
if (values[i][0]=='Total'){
nr = i;
Logger.log(nr);
var output = values[nr][todaysColumn];
// Do something with the output here I"m assuming you email it
}
}
The loop will keep going and find every "Total" and do the same thing. This answer assumes that the "Totals" are in the same column. You can get fancier with this if you only want certain tables to send and not others, but this should get you started.
I didn't quite understand the second part of your question...
"Also, is there any solution to automatically find the the array
number which contain date row for each table (instead defining this
manually). Hope my explanation make sense."
I'm guessing you want all the rows that contain "Total" in the specific column. You could instantiate a variable as an empty array like so, var totals = [];. Then instead of sending the email or whatever in the first loop you would push the row values to the array like so, totals.push(nr+1) . //adding 1 gives you the actual row number (rows count from 1 but arrays count from 0). You could then simply loop through the totals array and do whatever you wanted to do. Alternatively you could create an array of all the values instead of row numbers like totals.push(values[nr][todaysColumn]) and loop through that array. Lots of ways to solve this problem!
Ok based on our conversation below I've edited the "test" sheet and updated the code. Below are my edits
All edits have been made in your test sheet and verified working in Logger. Let me know if you have any questions.
Spreadsheet:
Added "Validation" Tab
Edited "Table" tab so the row with "Email Address" in Column A lines up with the desired lookup values (dates or categories)...this was only for the first two tables as all the others already had this criteria.
Code:
Create table/category selector...
In the editor go to File >> New >> HTMLfile
Name the file "inputHTML"
Copy and paste the following code into that file
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form class="notice_form" autocomplete="off" onsubmit="formSubmit(this)" target="hidden_iframe">
<select id="tables" onchange="hideunhideCatagory(this.value)" required></select>
<p></p>
<select id="categories" style="display:none"></select>
<hr/>
<button class="submit" type="submit">Get Total</button>
</form>
<script>
window.addEventListener('load', function() {
console.log('Page is loaded');
});
</script>
<script
src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
// The code in this function runs when the page is loaded.
$(function() {
var tableRunner = google.script.run.withSuccessHandler(buildTableList);
var catagoryRunner = google.script.run.withSuccessHandler(buildCatagoryList);
tableRunner.getTables();
catagoryRunner.getCategories();
});
function buildTableList(tables) {
var list = $('#tables');
list.empty();
list.append('<option></option>');
for (var i = 0; i < tables.length; i++) {
if(tables[i]==''){break;}
list.append('<option>' + tables[i] + '</option>');
}
}
function buildCatagoryList(categories) {
var list = $('#categories');
list.empty();
list.append('<option></option>');
for (var i = 0; i < categories.length; i++) {
if(categories[i]==''){break;}
list.append('<option>' + categories[i] + '</option>');
}
}
function hideunhideCatagory(tableValue){
var catElem = document.getElementById("categories");
if(tableValue == "Total Calls By Date" || tableValue == "Total Appointments by Date"){
catElem.style.display = "none"
document.required = false;
}else{
catElem.style.display = "block"
document.required = true;
}
}
function formSubmit(argTheFormElement) {
var table = $("select[id=tables]").val(),
catagory = $("select[id=categories]").val();
console.log(table)
google.script.run
.withSuccessHandler(google.script.host.close)
.getTotal(table,catagory);
}
</script>
</body>
<div id="hiframe" style="display:block; visibility:hidden; float:right">
<iframe name="hidden_iframe" height="0px" width="0px" ></iframe>
</div>
</html>
Edits to Code.gs file
Replace code in Code.gs with this...
//This is a simple trigger that creates the menu item in your sheet
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run Scripts Manually')
.addItem('Get Total','fncOpenMyDialog')
.addToUi();
}
//This function launches the dialog and is launched by the menu item
function fncOpenMyDialog() {
//Open a dialog
var htmlDlg = HtmlService.createHtmlOutputFromFile('inputHTML')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(150);
SpreadsheetApp.getUi()
.showModalDialog(htmlDlg, 'Select table to get total for');
};
//main function called by clicking "Get Total" on the dialogue...variables are passed to this function from the formSubmit in the inputHTML javascript
function getTotal(table,catagory) {
function toDateFormat(date) {
try {return date.setHours(0,0,0,0);}
catch(e) {return;}
}
//get all values
var values = SpreadsheetApp
.openById("10pB0jDPG8HYolECQ3eg1lrOFjXQ6JRFwQ-llvdE2yuM")
.getSheetByName("Tables")
.getDataRange()
.getValues();
//declare/instantiate your variables
var tableHeaderRow, totalRow, tableFound = false;
//begin loop through column A in Tables Sheet
for (var i = 0; i<values.length; i++){
//test to see if values have already been found if so break the loop
if(tableFound == true){break;}
//check to see if value matches selected table
if (values[i][0]==table){
//start another loop immediately after the match row
for(var x=i+1; x<values.length; x++){
if(values[x][0] == "Email Address"){ //This header needs to consistantly denote the row that contains the headers
tableHeaderRow = x;
tableFound = true;
}else if(values[x][0] == "Total"){
totalRow = x;
break;
}
}
}
}
Logger.log("Header Row = "+tableHeaderRow)
Logger.log("Total Row = "+ totalRow)
var today = toDateFormat(new Date())
var columnToTotal;
if(catagory==''){
columnToTotal = values[tableHeaderRow].map(toDateFormat).map(Number).indexOf(+today);
}else{
columnToTotal = values[tableHeaderRow].indexOf(catagory);
}
var output = values[totalRow][columnToTotal];
Logger.log(output);
var emailDate = Utilities.formatDate(new Date(today),"GMT+1", "dd/MM/yyyy");
//here is where you would put your code to do something with the output
}
/** The functions below are used by the form to populate the selects **/
function getTables(){
var cFile = SpreadsheetApp.getActive();
var cSheet = cFile.getSheetByName('Validation');
var cSheetHeader = cSheet.getRange(1,1,cSheet.getLastRow(),cSheet.getLastColumn()).getValues().shift();
var tabelCol = (cSheetHeader.indexOf("Tables")+1);
var tables = cSheet.getRange(2,tabelCol,cSheet.getLastRow(),1).getValues();
return tables.filter(function (elem){
return elem != "";
});
}
function getCatagories(){
var cFile = SpreadsheetApp.getActive();
var cSheet = cFile.getSheetByName('Validation');
var cSheetHeader = cSheet.getRange(1,1,cSheet.getLastRow(),cSheet.getLastColumn()).getValues().shift();
var catagoriesCol = (cSheetHeader.indexOf("Catagory")+1);
var catagories = cSheet.getRange(2,catagoriesCol,cSheet.getLastRow(),1).getValues();
return catagories.filter(function (elem){
return elem != "";
});
}
I'm trying to make a small script that allows for a little notes section. This section would have an input box that allows for adding elements to the list; which will be saved in localStorage so they are not lost when I refresh or close the browser. The code I have is as follows (it's all done through JS even the html, but ignore that.)
var notes = [];
var listthings = "<h2 id=\"titlething\">Notes</h2>" +
"<ul id=\"listing\">" +
"</ul>"
"<input type=\"text\" name=\"item\" id=\"textfield\">" +
"<input type=\"submit\" id=\"submitthing\" value=\"Submit\">";
JSON.parse(localStorage.getItem('notes')) || [].forEach( function (note) {
"<li id=\"listitem\">" + notes + "</li>";
})
$('#submitthing').click(function() {
notes.push($('#textfield').val());
});
localStorage.setItem('notes', JSON.stringify(notes));
Also, how would I go about appending the latest added li between the opening and closing tag? Obviously I'd usually do it using jQuery, but this is puzzling me a little. However, only the 'Notes' loads at the top, any ideas?
Your approach is way off the mark. You don't need JSON at all (this just confuses things) and you don't need to manually create HTML.
Also, you can use an array to store the notes, but since localStorage is the storage area, so an array is redundant. Additionally, without using an array, you don't need JSON. The entire problem becomes much easier to solve.
Unfortunately, the following won't run here in this snippet editor, due to security issues, but it would do what you are asking. This fiddle shows it working: https://jsfiddle.net/Lqjwbn1r/14/
// Upon the page being ready:
window.addEventListener("DOMContentLoaded", function(){
// Get a reference to the empty <ul> element on the page
var list = document.getElementById("notes");
// Loop through localStorage
for (var i = 0; i < localStorage.length; i++){
// Make sure that we only read the notes from local storage
if(localStorage.key(i).indexOf("note") !== -1){
// For each item, create a new <li> element
var item = document.createElement("li");
// Populate the <li> with the contents of the current
// localStorage item's value
item.textContent = localStorage.getItem(localStorage.key(i));
// Append the <li> to the page's <ul>
list.appendChild(item);
}
}
// Get references to the button and input
var btn = document.getElementById("btnSave");
var note = document.getElementById("txtNote");
// Store a note count:
var noteCount = 1;
// When the button is clicked...
btn.addEventListener("click", function(){
// Get the value of the input
var noteVal = note.value;
// As long as the value isn't an empty string...
if(noteVal.trim() !== ""){
// Create the note in localStorage using the
// note counter so that each stored item gets
// a unique key
localStorage.setItem("note" + noteCount, noteVal);
// Create a new <li>
var lstItem = document.createElement("li");
// Set the content of the <li>
lstItem.textContent = noteVal;
// Append the <li> to the <ul>
list.appendChild(lstItem);
// Bump up the note counter
noteCount++;
}
});
});
<input type=text id=txtNote><input type=button value=Save id=btnSave>
<ul id=notes></ul>
This is how I would approach it using jquery. but depens how complex this should be. this is just simple demo.
<input type="text" id="note" />
<button id="add">add note</button>
<ul id="notes"></ul>
javascript and jquery
function addNote(){
var data = localStorage.getItem("notes")
var notes = null;
if(data != null)
{
notes = JSON.parse(data);
}
if(notes == null){
notes = [];
}
notes.push($("#note").val());
localStorage.setItem("notes", JSON.stringify(notes));
refreshNotes();
}
function refreshNotes(){
var notesElement =$("#notes");
notesElement.empty();
var notes = JSON.parse(localStorage.getItem("notes"));
for(var i = 0; i< notes.length; i++){
var note = notes[i];
notesElement.append("<li>"+note+"</li>");
}
}
$(function(){
refreshNotes();
$("#add").click(function(){
addNote();
});
})
example:
http://codepen.io/xszaboj/pen/dOXEey?editors=1010
I am trying to add a Mark all/Unmark all button in sub-list which is a type of inline-editor sub-list. below I have added a code for list type sub-list which will not work on inline-editor sub-list. Can anyone help to find this?
function button1Func(type) {
if (type=='edit' || 'view')
{
var record = nlapiLoadRecord(nlapiGetRecordType(), nlapiGetRecordId());
var intCount = record.getLineItemCount('item');
var headrow = document.getElementById("item_headerrow");
var head = headrow.insertCell(0);
head.innerHTML ="Select";
for (var rep = 1; rep <= intCount; rep++)
{
var row = document.getElementById("item_row_"+rep);
var x = row.insertCell(0);
var newCheckbox = document.createElement("INPUT");
newCheckbox.setAttribute("type", "checkbox");
newCheckbox.setAttribute("id", "select_CheckBox"+rep);
x.appendChild(newCheckbox);
}
}
}
function button2Func(type) {
if (type=='edit' || 'view')
{
var record = nlapiLoadRecord(nlapiGetRecordType(), nlapiGetRecordId());
var intCount = record.getLineItemCount('item');
for (var rep = 1; rep <= intCount; rep++)
{
var repId = record.getLineItemValue('item', 'item', rep);
if(document.getElementById("select_CheckBox"+rep).checked==true){
makecopyfun(repId);
}
else
{
continue;
}
}
alert("Success");
}
}
function makecopyfun(repId){
var record = nlapiLoadRecord(nlapiGetRecordType(), nlapiGetRecordId());
var intCount = record.getLineItemCount('item');
record.insertLineItem('item',intCount + 1);
alert (intCount);
record.setCurrentLineItemValue('item','item',repId);
record.commitLineItem('item');
var id = nlapiSubmitRecord(record, true);
}
Not sure through the API because there's no record object, you could try using jQuery.
First write following code & create userEvent script and apply function name(initnoload) in beforeLoad Event.
Then Deploy that script on Quote.
function initonload(type, form, request) {
if (type=='edit' || type=='view') {
var list = form.getSubList("item");
list.addButton('custpage_markmark','Mark all','markall();'); //markall(); is function name from the client script
list.addButton('custpage_unmarkmark','Unmark all','unmarkall();'); //unmarkall(); is function name from client script
form.setScript('customscript_mark_all_item_quote'); // 'customscript_mark_all_item_quote' is the ID of script
}
}
Above code will add two buttons to Sublist and their action get executed in client script whose ScriptId we have Defined.
Now write the following code & create client script.(Note: Just save the client script, Don't specify any event function name and do not deploy it).
function markall() {
var count=nlapiGetLineItemCount('item'); //gets the count of lines
for(var i=1;i<=count;i++) {
nlapiSelectLineItem('item',i);
nlapiSetCurrentLineItemValue('item','custcol_checkbox_field','T',true,true); //'custcol_checkbox_field' is checkbox's field ID.
}
nlapiCommitLineItem('item');
}
function unmarkall() {
var count=nlapiGetLineItemCount('item');
for(var i=1;i<=count;i++) {
nlapiSelectLineItem('item',i);
nlapiSetCurrentLineItemValue('item','custcol_checkbox_field','F',true,true); //'custcol_checkbox_field' is checkbox's field ID.
}
nlapiCommitLineItem('item');
}
After saving the client script please paste it's ID in user event's form.setScript('Client script ID'). function
i hope this will help you out.
Please let me know if u face any difficulty.
Thank you.
I do came up with other idea as you can use the field which'll help you to mark/unmark all lines under sublist..you can add subtab to the form and under subtab you can add field and sublist. later you can apply script on that field which will help you to mark/unmark all sublist lines.
Here is the code...
form.addSubTab('custpage_tab', 'Main Tab');
form.addField('custpage_chkmark','checkbox','Mark/Unmark All',null,'custpage_tab');
form.addSubList('custpage_sublst','inlineeditor','SampleSubList','custpage_tab');
I'm trying to create something to refresh the list of dates to all users every 30 seconds.
I dynamically create a table with the list of dates in my database using AJAX, the thing is that the refresh removes what the user was writing in the moment of the refresh so I'm saving what the user writes in javascript global variables, calling the refresh function, then filling the inputs with the information in the variables and focusing the input the user was on.
The thing is the inputs aren't filled nor focused.
this is my relevant code here:
var identificacionc = "";
var nombresc = "";
var apellidosc = "";
var telefonoc = "";
var posicionc = 0;
var ladoc = 0;
//This is called on input onfocus to record the id
function recuerdo(posicion, lado)
{
posicionc = posicion;
ladoc = lado;
}
function actualizar()
{
//This line is not relevant
listaragenda();
if (document.getElementById("datepicker").value != "")
{
//put the info in the global variables and it works even if they're dynamically created
identificacionc = document.getElementById("txtidentificacion" + posicionc).value;
nombresc = document.getElementById("txtnombres" + posicionc).value;
apellidosc = document.getElementById("txtapellidos" + posicionc).value;
telefonoc = document.getElementById("txttelefono" + posicionc).value;
//Here is where I call the function to refresh dates
listarcitas();
}
}
function listarcitas()
{
var objAjax = crearObjeto();
var fecha = document.getElementById("datepicker").value;
objAjax.open("POST", "clases/listarcitas.php", true);
objAjax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
objAjax.onreadystatechange = function()
{
if (objAjax.readyState == 4 && objAjax.status == 200)
{
document.getElementById("citaslistadas").innerHTML = objAjax.responseText;
//Checks if any global variable is not empty to start to fill them with the info
//nothing inside this If works
//posicionc and ladoc have the correct values
if (identificacionc != "")
{
document.getElementById("txtidentificacion" + posicionc).value = identificacionc;
document.getElementById("txtnombres" + posicionc).value = nombresc;
document.getElementById("txtapellidos" + posicionc).value = apellidosc;
document.getElementById("txttelefono" + posicionc).value = telefonoc;
if (ladoc == 1)
{
document.getElementById("txtidentificacion" + posicionc).focus();
}
else if (ladoc == 2)
{
document.getElementById("txtnombres" + posicionc).focus();
}
else if (ladoc == 3)
{
document.getElementById("txtapellidos" + posicionc).focus();
}
else if (ladoc == 4)
{
document.getElementById("txttelefono" + posicionc).focus();
}
}
}
}
objAjax.send("fecha=" + fecha);
}
//the interval every 30s
window.setInterval("actualizar()", 30000);
Everything retrieved from AJAX works fine everything is listed, even in the web browser console I make alerts of the variables, set the values and focus the dynamically created inputs, everything works fine.
But why this is not working in the code?
Thanks in advance