I have a spreadsheet that displays every member of my staff's name and what time their lunch break is. We use this list to assign them new cases that come in, but obviously we can't assign them a case when they are at lunch.
I would like to figure out a way to highlight or "gray out" their names when they're on lunch, and then change back when their lunch time is over.
I think you can use a conditional class with css and JavaScript maybe inside window.onLoad.. Something like that:
CSS:
.class{
...
}
.classHighLight{
...
}
JS:
window.onload = function() {
var currentHour = new Date().getHours();
var arrStaffs = Here you load your spreadsheet's hours
arrStaffs.forEach((member) => {
if(member.lunchHour == currentHour){
document.getElementById(*Member's button ID*).className = 'classHighLight';
}
})
};
That way, when you visit the page, your onLoad function will highligth members that have the same lunchHour as the current hour. I dont't know your spreadsheet data layout, but I'm pretending you have something like:
Mike - 14 / Peter - 12
Hope it helps you :)
Related
I am at starting web dev, already using html/css.
For a little project, I had a look at JavaScript. (My goal is that when people click a button, the site will show a random sentence that will be taken from a google sheet cell.)
Could you tell me please if it is even possible? If so, please share some ideas that I will explore. If not, please give me some alternative ideas... Thanks so much.
Have a good day!
-LeganV9
This is possible using Google Apps Script!
I have a working demo here, with the source being here. I dare you to get the jackpot. :D
In order to make this, you can go to https://script.new. Now, in code.gs put this:
function doGet() {
return HtmlService.createTemplateFromFile("index").evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getVals(){
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1IDbhQhaImcQB-4j-iByajwAkvxkutptcPMhMTxNrPtU/edit#gid=0");//Put your URL here
var sheet = ss.getSheetByName("Sheet1");//Put your sheet name here
var AMOUNT_OF_SENTENCES = sheet.getMaxRows().toString().replace(".0","");//You can replace this with a number eg 20
var range = sheet.getRange(1, 1,AMOUNT_OF_SENTENCES);
var values = range.getValues();
var newValues = [];
for(var i = 1; i<values.length;i++){
if(values[i][0] === "" || values[i][0] === " "){
}else{
newValues.push(values[i][0]);
}
}
return {valuesVar: newValues };
}
After that, create a new HTML file called "index" and put this in it:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>
The results are: <span id = "results">Loading...</span>
</h1>
<button id = "yourIdHere">Click me!!</button>
<script>
var yourDataList;
function onSuccess(data) {
yourDataList= data.valuesVar;
}
google.script.run.withSuccessHandler(onSuccess).getVals();
var myBtn = document.querySelector("#yourIdHere"); //Declare button, replace yourIdHere with your ID
var randomNum = 0; //Decclre random number
function getRandom() { //Declare your function
randomNum = parseInt(Math.random() * yourDataList.length); //Get random number between 0 and the length of your datalist
return yourDataList[randomNum]; //Return your value
}
myBtn.addEventListener("click", function() { //Bind a listener to the button
document.querySelector("#results").innerText = getRandom(); //Do whatever you want to with the random value
});
document.querySelector("#results").innerText = getRandom();//Change from loading...
</script>
</body>
</html>
Welcome to the world of web development! Hope your project is a success.
It should definitely be possible, since Google Sheets offers an API which has read/write functionality (https://developers.google.com/sheets/api).
You could even later extend this so people can submit their own sentences, given that writing to a Google Sheet is also possible with this API.
However, since you're starting out, consider treating this as an iterative process. You don't have to publish your first version, but just to prevent overwhelming yourself, you might want to set small milestones along the way - each adding more functionality. For example:
Create an array of random sentences (you could, for example, start with using this to keep it simple: https://github.com/JamesFT/Database-Quotes-JSON).
Select and log a random sentence to the console (console.log()) each time the script is executed.
Transfer the random sentence to render in HTML and allow a new sentence to be generated each time a button is pressed.
Move your sentences into a Google Sheet and begin exploring the API.
This way, you achieve something in a much shorter space of time, while working towards your end goal. It's a good way to keep motivated and make things more manageable.
Best of luck!
I have written a very simple code on my Google Sheets file. This is the purpose:
Save some cells values from StaticSheet (all the Copyxxx) that need to be copied in DynamicSheet.
Get the value of one specific cell inserted by the user manually.
Enter a While loop useful only to increase an indicator and get the number of the row where I want to copy those values previously saved.
Copy those values on this row but different columns.
The problem is that it seems that most of the time it does not even run the script after I told it to do so.
What is funny is that sometimes it works, super slowly, but it works for like a couple of minutes. And after it stops working again.
Could you please tell me what am I missing here please?
function Copy_Static_on_Dynamic() {
var app = SpreadsheetApp;
var ss = app.openById("xxxxyy--------yyzzzz")
var StaticSheet = ss.getSheetByName("DEAT Price");
var DynamicSheet = ss.getSheetByName("DEAT Price + TEST");
var CopySKU = StaticSheet.getRange(5,1,40);
var CopyPrices = StaticSheet.getRange(5,3,40,4);
var CopyUsage = StaticSheet.getRange(5,8,40);
var Week_1 = StaticSheet.getRange(2,4).getValues();
var i = 1;
Logger.clear();
while(DynamicSheet.getRange(i,3).getValues() != Week_1)
{
Logger.log(i);
i+=1;
}
CopySKU.copyTo(DynamicSheet.getRange(i,4,40));
CopyPrices.copyTo(DynamicSheet.getRange(i,6,40,4));
CopyUsage.copyTo(DynamicSheet.getRange(i,11,40));
}
If you see the "Preparing for Execution" message in the Apps Script editor, you can reload the browser window and run the function again. The program will likely go away.
So I think I have solved it.
As Serge insas was saying I had my script running on the background, I found it out on the "Execution" section, where you can also interrupt them.
After I discover it I kept testing, and I saw that the while loop needed almost 2 seconds to check the condition every time, making the script incredibly long.
So instead of:
while(DynamicSheet.getRange(i,3).getValues() != Week_1)
... I have created a variable declared previously such as:
var WeekLOOP = DynamicSheet.getRange(i,3).getValues();
while(WeekLOOP != Week_1) { --- }
... and now the script needs few milliseconds to run the condition. I don't have enough technical knowledge to say if this was the only issue, but is what apparently solved my problem.
Thanks to all for the support! Will come back if I need any further help :)
As was mentioned by Amit Agarwal, to solve the error message mentioned on the question, refresh the web browser tab.
Regarding the code,
On
var Week_1 = StaticSheet.getRange(2,4).getValues();
and
DynamicSheet.getRange(i,3).getValues()
instead of getValues you should use getValue because your code are referring to single cell cells otherwise you will be getting 2D arrays instead of scalar values.
The use of while should be made very carefully to avoid functions running endlessly. You could add some "safeguard" like the following
var max_iterations = 100 // Edit this
while(DynamicSheet.getRange(i,3).getValue() != Week_1 && i <= max_iterations) {
Let me start by saying, that while I have some programming experiencing (some basic C from a college class and I once wrote a FORTRAN programm in college for a professor), I am utterly new to JS and beginning to get a bit frustrated.
For some reason, even after reading tutorials and watching several YouTube videos on objects, I seem unable to wrap my head around it. I understand the fundamentals and have no problems doing very basic stuff, like writing a loop that prints out increments on a HTML site, but every time I try something practical, I am completely at a loss.
Here is my current problem: I have created this HTML site that generates a shopping list. Basically, when I click on one of the buttons next to an item name, it adds that item to the list in the middle of my screen. Thanks to Google I found a piece of JavaScript code which, through try and error, I managed to tweak for this purpose:
<!-- click this button to add the item-->
<button onclick="myFunction('ITEM1', 100)" class="sidebarbuttons" >ITEM1 </button>
/* Create a List one line at a time- */
<script>
function myFunction( x, y ) {
var node = document.createElement("LI" );
var textnode = document.createTextNode(x);
node.appendChild(textnode);
document.getElementById("myList").appendChild(node);
}
</script>
So far, so good. Now I want to get the net price for all the items. Which means, When I click the button, I want a function to add the price of that item to a variable and then display that variable in a field with
document.getElementById("result").innerHTML = total_sum;
Here's my question: how, oh my god, how do I do this? I thought I could add the following:
function myfunction(x,y){
var sum = 0;
var sum+=y;
}
document.getElementById("result").innerHTML = 'sum';
Obviously, this doesn't work at all. Can you please give me some hints what I have to do to make this work?
First of all,
please consider to study JavaScript better, because it's a falsy easy programming language and it's very dangerous to copy&paste without knowing the language. It's quite normal to read a lot, watch a lot and don't know where to start, and it's the main reason because people hates JavaScript: because we don't know well JavaScript. So consider to read the book series "You Don't Know" by Kyle Simpson.
About your question. You can add a variable to storage the sum of your items and when you click to an item, you can add to it:
var total_sum = 0;
function myFunction( x, y ) {
var node = document.createElement("LI" );
var textnode = document.createTextNode(x);
node.appendChild(textnode);
document.getElementById("myList").appendChild(node);
showResults(y);
}
function showResults(price){
total_sum += parseFloat(price)
document.getElementById("result").innerHTML = total_sum;
}
JSBIN
Let me know ;)
So you are on the right track. Picking up where you left off in your last code block, there are few things you will need to change.
//declare the variable outside of the function... otherwise it will only be available to you within that function.
var totalSum = 0;
// then within your function you will be able to successfully add to the global totalSum variable
function calculateSum(x){
totalSum += x;
// and lastly... set the innerHTML within the function... which should equal the variable totalSum
document.getElementById("result").innerHTML = totalSum;
}
Hope this helps.
I had this problem which Cooper helped me to solve it (thanks again for that), but now I'm struggling with a different one. The following script will count how many times a client code will appear on another Spreadsheet using as a second condition yesterday date.
function countSheets()
{
var vA = appSh();
var td = Utilities.formatDate(subDaysFromDate(new Date(),2), Session.getScriptTimeZone(), "dd/MM/yyyy");
var mbs=getAllSheets();
//var s='';
for (var i=2;i<vA.length;i++)
{
var d = Utilities.formatDate(new Date(vA[i][12]), Session.getScriptTimeZone(), "dd/MM/yyyy");
for(var key in mbs)
{
if(vA[i][0]==key && d==td)
{
mbs[key]+=1;
}
}
}
return mbs;
}
Then I have the below code which will search in the main spreadsheet (a table) a string and when was found will return row number, also will search for the date yesterday and return the column number. Based on these information I'll get the range where I need to paste the count result from the first script.
function runScript()
{
var ss=SpreadsheetApp.openById('ID');
var mbs=countSheets();
for(var key in mbs)
{
var sh=ss.getSheetByName(key);
var rg=sh.getDataRange();
var vA=rg.getValues();
for(var i=0;i<vA.length;i++)
{
if(vA[i][1]=='Total Number of Applications')
{
var nr=i;
break;//by terminating as soon as we find a match we should get improved performance. Which is something you cant do in a map.
}
}
if(typeof(nr)!='undefined')//If we don't find a match this is undefined
{
var today=subDaysFromDate(new Date(),2).setHours(0,0,0,0);
for(var i=0;i<vA[3].length;i++)
{
if(vA[3][i])//Some cells in this range have no contents
{
if(today.valueOf()==new Date(vA[3][i]).valueOf())
{
sh.getRange(nr+1,i+1,1,1).setValue(Number(mbs[key]));
}
}
}
}
}
return sh;
}
PROBLEM: I have 24 rows on the main Spreadsheet. So I will need to write the same script 24 times. As example, I need to count Total Number of Applications, Total Number of Calls, Number of Live Adverts and so on. If I do this it will exceed execution time since each script takes on average 25 seconds to run.
I did some researches on this website and internet and read about storing values and re-use them over and over. At the moment my script will have to go every time through the same file and count for each condition.
Q1: Is there any chance to create another array that contain all those strings from the second script?
Q2: How to use PropertiesService or anything else to store data and don't have to run over and over getValues() ? I've read Google Documentation but couldn't understand that much from it.
I hope it all make sense and can fix this problem.
My best regards,
Thank you!
My Approach to your Problem
You probably should write it for a couple of rows and then look at the two of them and see what is unique to each one. What is unique about each one is what you have to figure out how to store or access via an external function call.
The issue of time may require that you run these functions separately. I have a dialog which I use to load databases which does exactly that. It loads 800 lines and waits for 10 seconds then loads another 800 lines and wait for ten seconds and keeps doing that until there are no more lines. True it takes about 10 minutes to do this but I can be doing something else while it's working so I don't really care how long it takes. I do care about minimizing my impact to the Google Server though and so I don't run something like this just for fun.
By the way the 10 second delay is external to the gs function.
I'm having difficulty setting a time (duration) value in a datebox. A simple demonstration of the problem is if I do something like:
function initDuration() {
this.d['header Text'] = "Set";
this.d['headerText'] = "Set Duration";
var element = 'input#'+this.element[0].id;
var currentDt = $(element).datebox('getTheDate');
// ***************
var dt = $(element).datebox('parseDate', '%H:%M', this.element[0].value); // Where this.element[0].value = "01:00:00"
// ***************
$(element).datebox('setTheDate', this.element[0].value);
$(element).trigger('datebox', { 'method': 'doset' });
}
dt just contains the current date/time; i.e. jtsage didn't like it. The element is defined (in jade) as:
input.Duration(type="text" name="duration" form="form#{i}"
id="duration#{i}" value="#{map[i].duration}" data-role="datebox"
data-options=
'{"mode":"durationflipbox", "overrideDurationOrder":["h","i"],'
+' "overrideTimeFormat": "%l:%M", "minuteStep":15, "beforeOpenCallback": "initDuration"}')
Also I'm not sure how to change the flipbox title. The 2nd line in initDuration() sets the text for the button but the title still says 'Set Time'.
Because of the first problem the last 2 lines in initDuration() don't do what I want. i.e. they just use the current time, whatever that happens to be.
My apologies that this is going to be an incomplete answer, but it was going to be too long for a comment.
For the title - give "overrideHeaderText" a shot instead. It is entirely possible that I screwed this up at some point, it's not a feature I use in any of my own projects.
Next...
var dt = $(element).datebox('parseDate', '%H:%M', this.element[0].value); // Where this.element[0].value = "01:00:00"
I think I am reading you correctly that "dt" isn't containing what you are expecting. It's because 01:00:00 != %H:%M - to read this "format", you'd need to either use "%H:%M:%S" or "%H:%M:00" (the later ignoring the seconds field).
That said, I think what you are trying to do is set a duration, which, is a little different. There are a few ways to do it - and I'm noticing that there isn't a lot of support to do it functionally. The simplest method, is the set the value of the input, and let datebox handle the math - just be aware that the format you drop into the input must be exactly the same as the output format - it will read it when the control opens (or is initialized if the control is being shown inline - if you are doing it inline, and set the value "later", you can use the 'refresh' method to update it).
For what it's worth, if you really, really, really want to use the setTheDate method, duration modes work by comparing "theDate" (the publicly available date, i.e. setTheDate, getTheDate) with an internal initDate - which is not exposed to the API, but can be found here:
$(element).data('jtsage-datebox').initDate
So, in pseudo-code, for a duration of an hour
myNewDate = $(element).data( 'jtsage-datebox' ).initDate;
myNewDate.setHour( myNewDate.getHour() + 1 );
$(element).datebox( 'setTheDate', myNewDate );