Passing value on button click from google script file to sidebar - javascript

I am trying to pass a list containing the start cell and the end cell of the active range, and putting each of the values in a input field.
document.getElementById("btn-get-range").addEventListener('click', function(){
google.script.run.withSuccesHandler(showValues).getSelectedRange();
});
function showValues(startEndList){
document.getElementById('input-start-cell').value = startEndList[0];
document.getElementById('input-end-cell').value = startEndList[1];
}
/* Google Script Code */
let UI = SpreadsheetApp.getUi();
let SHEET = SpreadsheetApp.getActiveSheet();
function onOpen() {
UI.createMenu('Get Range In Input')
.addItem('Show Sidebar', 'showSidebar')
.addToUi();
}
function showSidebar(){
let html = HtmlService.createHtmlOutputFromFile('sidebar')
.setTitle('Get Range In Input');
UI.showSidebar(html);
}
function getSelectedRange() {
let range = SpreadsheetApp.getActive().getActiveRange().getA1Notation();
let startEndList = range.split(':');
return startEndList
}
<div class="text-center">
<input id='input-start-cell' type='text' class="m-2">
<input id='input-end-cell' type='text' class="m-2">
<button id='btn-get-range' type="button" class="btn btn-primary w-50" class="m-2">Get Range</button>
</div>
Why is it not working?
Thx for yout time.

There is a spelling mistake for withSuccesHandler. Modify withSuccesHandler to withSuccessHandler.
So this:
document.getElementById("btn-get-range").addEventListener('click', function(){
google.script.run.withSuccesHandler(showValues).getSelectedRange();
});
function showValues(startEndList){
document.getElementById('input-start-cell').value = startEndList[0];
document.getElementById('input-end-cell').value = startEndList[1];
}
Should be:
document.getElementById("btn-get-range").addEventListener('click', function(){
google.script.run.withSuccessHandler(showValues).getSelectedRange();
});
function showValues(startEndList){
document.getElementById('input-start-cell').value = startEndList[0];
document.getElementById('input-end-cell').value = startEndList[1];
}

Related

I've run a script for Google Sheets sidebar that is supposed to not show hidden tabs but they still show. Asking the community how to fix this

I have a question about: Google Sheets Index Sidebar of all tabs - add search functionality
The last answer in the above includes the code below and this note: "After some trial and error, I was able to tweak it to ensure hidden sheets aren't included."
When I run the code, my sidebar still shows hidden tabs. Am not a programmer so I would respectfully ask that answers take this into consideration. Thank you. Sidebar is a terrific addition; really appreciate the author & contributors. Here is the code:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Sidebar Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var ui = HtmlService.createTemplateFromFile('Sidebar.html')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('Index Sidebar');
SpreadsheetApp.getUi().showSidebar(ui);
}
function getSheetNames() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets().filter(s => !s.isSheetHidden());
return sheetNamesIds(sheets);
}
// function to create array of sheet names and sheet ids
function sheetNamesIds(sheets) {
var indexOfSheets = [];
// create array of sheet names and sheet gids
sheets.forEach(function(sheet){
indexOfSheets.push([sheet.getSheetName(),sheet.getSheetId()]);
});
//Logger.log(indexOfSheets);
return indexOfSheets;
}
// function to return a button with onclick attribute for each sheet that matches
function returnListItems(text) {
var sheetNames = getSheetNames()
// Checking if there is a search term
if (text) {
sheetNames = sheetNames.filter(n => n[0].includes(text))
}
var htmlString = sheetNames.map(function(d) {
var string = `
<li>
<input
type="button"
value="${d[0]}"
onclick=\"google.script.run.setActiveByName('${d[0]}')\"/>
</li>
`
return string }).join(' ')
return htmlString
}
// Utility function to set Active sheet by name.
function setActiveByName(name) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name)
SpreadsheetApp.setActiveSheet(ss)
}
The above is all of the code I'm aware of other than the HTML and JS below.
HTML
<!DOCTYPE html>
<h1>Index of all sheets in this workbook:</h1>
<script>
function removeElement(elementId) {
var element = document.getElementById(elementId);
element.parentNode.removeChild(element);
}
function buildList(text) {
google.script.run.withSuccessHandler(onSuccess).returnListItems(text)
}
function onSuccess(result) {
var element = document.createElement("ol")
element.innerHTML = result
var sidebar = document.getElementById("sidebar")
sidebar.appendChild(element)
}
function getTextAndSearch() {
var text = document.getElementById("text-search").value
removeElement("ol")
buildList(text)
}
</script>
<sidebar id="sidebar">
<input type="button" value="Close" onclick="google.script.host.close()" />
<br>
<input type="text" id="text-search" />
<input type="button" value="Search" onclick="getTextAndSearch()" />
<ol id="ol">
<?!=
returnListItems()
?>
</ol>
</sidebar>
JS
JS
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Sidebar Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var ui = HtmlService.createTemplateFromFile('sidebar.html')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('Index Sidebar');
SpreadsheetApp.getUi().showSidebar(ui);
}
function getSheetNames() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
return sheetNamesIds(sheets);
}
// function to create array of sheet names and sheet ids
function sheetNamesIds(sheets) {
var indexOfSheets = [];
// create array of sheet names and sheet gids
sheets.forEach(function(sheet){
indexOfSheets.push([sheet.getSheetName(),sheet.getSheetId()]);
});
//Logger.log(indexOfSheets);
return indexOfSheets;
}
// function to return a button with onclick attribute for each sheet that matches
function returnListItems(text) {
var sheetNames = getSheetNames()
// Checking if there is a search term
if (text) {
sheetNames = sheetNames.filter(n => n[0].includes(text))
}
var htmlString = sheetNames.map(function(d) {
var string = `
<li>
<input
type="button"
value="${d[0]}"
onclick=\"google.script.run.setActiveByName('${d[0]}')\"/>
</li>
`
return string }).join(' ')
return htmlString
}
// Utility function to set Active sheet by name.
function setActiveByName(name) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name)
SpreadsheetApp.setActiveSheet(ss)
}
Hopefullly I've included everything needed. Thank you.

Is it possible to keep cursor in the text area after a click even?

I'm a front-end learner. I created a primitive app that I saw in Duolingo or Memrise. It consists of a text-area and buttons situated below with extra Spanish letters which can be used in necessary. I'm a Spanish learner and need to use the letters to type outside the language apps.
The problem is that when I type in the text and click on the button with a specific extra letter the cursor in the text area disappears. I need to click again in the text area. It's a bit annoying. In Duolingo it stays in the area and the use of mouse is reduced. Does anyone know how to fix it?
<div class="container">
<div class="input-container">
<div class="upper-box">
<textarea name="" id="textarea" rows="3"></textarea>
</div>
<div class="input-keyboard">
<button id="a">á</button>
<button id="e">é</button>
<button id="i">í</button>
<button id="o">ó</button>
<button id="u">ú</button>
<button id="n">ñ</button>
<button id="exclamation">¡</button>
<button id="question">¿</button>
<button id="clear">Clear</button>
<!-- input-keyboard ends below -->
</div>
</div>
</div>
const textarea = document.querySelector("#textarea");
// buttons
const clearBtn = document.querySelector("#clear");
const aBtn = document.querySelector("#a");
const eBtn = document.querySelector("#e");
const iBtn = document.querySelector("#i");
const oBtn = document.querySelector("#o");
const uBtn = document.querySelector("#u");
const nBtn = document.querySelector("#n");
const exlBtn = document.querySelector("#exclamation");
const queBtn = document.querySelector("#question");
aBtn.addEventListener("click", function () {
let inputText = (textarea.value += "á");
});
eBtn.addEventListener("click", function () {
let inputText = (textarea.value += "é");
});
iBtn.addEventListener("click", function () {
let inputText = (textarea.value += "í");
});
oBtn.addEventListener("click", function () {
let inputText = (textarea.value += "ó");
});
uBtn.addEventListener("click", function () {
let inputText = (textarea.value += "ú");
});
nBtn.addEventListener("click", function () {
let inputText = (textarea.value += "ñ");
});
exlBtn.addEventListener("click", function () {
let inputText = (textarea.value += "¡");
});
queBtn.addEventListener("click", function () {
let inputText = (textarea.value += "¿");
});
clearBtn.addEventListener("click", function () {
let inputText = (textarea.value = "");
});
In each button click event, you can set focus the textarea after inserting the respective letter. Check below example (With jQuery):
$(document).on("click", "#a", function () {
var text = $("#textarea").val();
$("#textarea").val(text + 'á');
$("#textarea").focus();
}
Even with Javascript you can have a similar approach.
document.getElementById("textarea").focus();

How can I execute an Apps Script Function using a built in HTML sidebar browser?

Currently, I am making an expense tracker on Google Sheets. Here is a preview:
I currently have a function that checks how much money (in percentage) is spent on a particular brand.
Like this:
Here is the code block for it:
function perCent(categ){
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var values = sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();
var total = 0;
var sum = 0;
values.forEach(function(row){
total+=row[1];
if (row[7]==categ){sum+=row[1]}
})
return (sum/total)*100 + "%" + " of your income has been spent on " + categ;
}
However, for this function to work, the user must manually type in a cell =perCent(categ), which is not user friendly.
For example:
To increase user friendliness, I introduced an HTML sidebar, as such:
Here is the code block for the sidebar:
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Main Menu')
.addItem('My sidebar 1', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Clothing Main Menu');
SpreadsheetApp.getUi()
.showSidebar(html);
}
Here is the HTML code block:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1 class = "align ">Welcome to the clothing expense custom menu bar!</h1>
<h2 class = "align">Here are the custom functions you can use:</h2>
<p class = "align"> Per Cent Function</p>
<form>
<input class = "margin" type = "text" placeholder="Enter brand name">
<div>
<button type = "submit" class="align">Submit
</button>
</div>
</form>
</body>
<style>
body{
background-color: black;
color: red;
}
.align{
text-align: center;
}
.margin{
margin-bottom: 10px;
}
</html>
Is there any way I can make the perCent function run by clicking on the Submit button?
Thanks!
You need add google.script.run in the script section of your html. This will run the function in your Code.gs. To prompt the value calculated by perCent() function, you can use UI.alert().
Example:
Sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
function runFunc(){
var brand = document.getElementById("brand").value;
google.script.run.perCent(brand);
}
</script>
<body>
<h1 class = "align ">Welcome to the clothing expense custom menu bar!</h1>
<h2 class = "align">Here are the custom functions you can use:</h2>
<p class = "align"> Per Cent Function</p>
<form onsubmit="runFunc()">
<input class = "margin" id="brand" type = "text" placeholder="Enter brand name">
<div>
<button type="submit" class="align">Submit</button>
</div>
</form>
</body>
<style>
body{
background-color: black;
color: red;
}
.align{
text-align: center;
}
.margin{
margin-bottom: 10px;
}
</html>
Code.gs
function perCent(categ){
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var values = sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();
var total = 0;
var sum = 0;
values.forEach(function(row){
total+=row[1];
if (row[7]==categ){sum+=row[1]}
})
var val = (sum/total)*100 + "%" + " of your income has been spent on " + categ;
var ui = SpreadsheetApp.getUi();
ui.alert(val)
}
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Main Menu')
.addItem('My sidebar 1', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Clothing Main Menu');
SpreadsheetApp.getUi()
.showSidebar(html);
}
Output:
References
Class google.script.run
Ui.alert(prompt)
I was able to get this to work as a UI popup. I'm sure it's possible as a sidebar but this is a quick way to accomplish what you are looking to do:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Clothing Main Menu')
.addItem('Calculate % spend per brand', 'showPrompt')
.addToUi();
}
function showPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt(
'Check percentage of clothing budget',
'Please enter a clothing brand:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
ui.alert('Results: ' + perCent(text) + '.');
} else if (button == ui.Button.CANCEL) {
// User clicked "Cancel".
ui.alert('Please input a clothing brand');
} else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
}
}
When you insert this apps script snippet a menu called "Main Menu" will be added and the only option will be called "Calculate % spend per brand". If the user clicks that they will be prompted to enter a brand name. When they do so and then hit OK the perCent function results will be displayed to them.
Edit:
You can get this to work as a sidebar, but as far as I can tell I cannot get a form field value inside of an html sidebar to be processed through an apps script function. This is as close as I could get, it pops up the input box same as last time but this time the results are displayed in the sidebar. It can obviously be styled and expanded upon but this is pretty close to what you were hoping for:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Clothing Main Menu')
.addItem('Calculate % spend per brand', 'showPrompt')
.addToUi();
}
function showPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt(
'Check percentage of clothing budget',
'Please enter a clothing brand:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
var html = HtmlService.createHtmlOutput("<h1> Results: "+ perCent(text) + "</h1>");
SpreadsheetApp.getUi().showSidebar(html);
} else if (button == ui.Button.CANCEL) {
// User clicked "Cancel".
ui.alert('Please input a clothing brand');
} else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
}
}
Edit 2:
Here is a version that works entirely within the HTML sidebar:
function perCent(categ){
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var values = sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();
var total = 0;
var sum = 0;
values.forEach(function(row){
total+=row[1];
if (row[7]==categ){sum+=row[1]}
})
var rtrnStr = (sum/total)*100 + "%" + " of your income has been spent on " + categ;
var html2 = HtmlService.createHtmlOutput("<h1> Results: "+ rtrnStr + "</h1>");
SpreadsheetApp.getUi().showSidebar(html2);
}
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Clothing Main Menu')
.addItem('My sidebar 1', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutput('<script> function processSub(){ var result = document.getElementById("brand").value; google.script.run.perCent(result);}</script> <form id="myForm" onsubmit="event.preventDefault(); processSub();"> <input type="text" label="Clothing Brand" id="brand"><br><input type="submit"></form> ')
.setTitle('Clothing Main Menu');
SpreadsheetApp.getUi()
.showSidebar(html);
}

bootstrap table add border to 'contenteditable' class after a change

I have this javascript that dynamically creates a table in HTML from a CSV file. I’m also using bootstrap and they have a cool ‘content editable’ class for table cells where you can edit the ’td’ element. I’m trying to add some css or just a border to a table cell after it has been edited. I’ve tried some jQuery but with no success. Any suggestions would be appreciated.
heres the github i used click here, essentially the same example with a few extra lines of javascript.
JavaScript:
<script type="text/javascript">
// check browser support
// console.log(SimpleExcel.isSupportedBrowser);
var fileInputCSV = document.getElementById('fileInputCSV');
// when local file loaded
fileInputCSV.addEventListener('change', function (e) {
// parse as CSV
var file = e.target.files[0];
var csvParser = new SimpleExcel.Parser.CSV();
csvParser.setDelimiter(',');
csvParser.loadFile(file, function () {
// draw HTML table based on sheet data
var sheet = csvParser.getSheet();
var table = document.getElementById('result');
var tbody = document.createElement('tbody');
table.innerHTML = "";
sheet.forEach(function (el, i) {
var row = document.createElement('tr');
el.forEach(function (el, i) {
var cell = document.createElement('td');
cell.setAttribute('contenteditable', 'true');
cell.setAttribute('id', 'cells');
cell.innerHTML = el.value;
row.appendChild(cell);
});
table.appendChild(tbody);
tbody.appendChild(row);
});
// create button to export as TSV
var btnSave = document.getElementById('fileExport');
btnSave.hidden = false;
btnSave.value = 'Save as TSV file ->';
document.body.appendChild(btnSave);
// export when button clicked
btnSave.addEventListener('click', function (e) {
var tsvWriter = new SimpleExcel.Writer.TSV();
tsvWriter.insertSheet(csvParser.getSheet(1));
tsvWriter.saveFile();
});
var data = csvParser.getSheet(1);
// var json_data = JSON.stringify(data);
console.log("data here", data);
angular.element('#angular-controller').scope().getCSV(data);
// print to console just for quick testing
// console.log(csvParser.getSheet(1));
// console.log(csvParser.getSheet(1).getRow(1));
// console.log(csvParser.getSheet(1).getColumn(2));
// console.log(csvParser.getSheet(1).getCell(3, 1));
// console.log(csvParser.getSheet(1).getCell(2, 3).value);
});
});
</script>
jQuery:
$(document).ready(function() {
$('#fileInputCSV').on('change',function() {
$('#save-button').css('display','inline-block');
$('#add-row').css('display', 'inline-block');
});
$('#cells').on('change', function() {
console.log("change");
$('#cells').css('style','border:2px solid orange');
});
});
HTML:
<div class="row">
<div class="container-fluid">
<button type="button" class="btn btn-success btn-sm" id="add-row" style="display:none" data-toggle="modal" data-target="#myModal">Add Row</button>
<button class="btn btn-sm btn-success pull-right" id="save-button" style="display:none">Save</button>
<table id="result" class="table table-condensed"></table>
<input type="button" id="fileExport" hidden="true" />
</div>
</div>
Have you tried to insert bootstrap´s table-bordered class to the object after is has been edited, like:
<table id="result" class="table table-condensed table-bordered"></table>
Check in the usagehere
Look at all bootstrap table possible flags here
To load a class dynamically to the result table (id="result") use:
$(#result).addClass("table-bordered");

Remove checked checkboxes

I am making a To-do list, where I want to be able to add new tasks, and delete tasks that are checked off. However, it seems my function just deletes all tasks, not just the ones that are checked off. Neither does it seem to allow new tasks to be added.
html:
<h1 id="title"> To-do list</h1>
<div id="task_area">
</div>
<input type="text" id="putin"></input>
<button id="add">add</button>
javascript:
<button id="clear">Clear completed tasks</button>
var tasks = document.getElementById("task_area")
var new_task = document.getElementById("add")
var clear = document.getElementById("clear")
new_task.addEventListener("click", function() {
var putin = document.getElementById("putin")
var input = document.createElement('input')
input.type = "checkbox"
var label = document.createElement('label')
label.appendChild(document.createTextNode(putin.value))
task_area.appendChild(input)
task_area.appendChild(label)
})
clear.addEventListener("click", function() {
for (i = 0; i < task_area.children.length; i++) {
if (task_area.children[i].checked === true) {
task_area.remove(tasks.children[i])
}
}
})
jsfiddle: https://jsfiddle.net/4coxL3um/
.remove removes the element you are calling it from, and doesn't take an argument for what to remove. The following:
task_area.remove(tasks.children[i])
should be
tasks.children[i].remove()
EDIT: As Mononess commented below, this will only remove the checkboxes and not the labels. While you could delete both using Jayesh Goyani's answer below, it's probably better that each input/label pair be wrapped in a single div or span for easier management.
You could try adding an event listener to each child of task_area that calls the below function. Haven't gotten a chance to test it out, and may not fulfill all of your requirements, but should get the job done.
function removeClicked() {
this.parentNode.removeChild(this);
}
Please try with the below code snippet. Below code will help you to remove selected checkbox with label.
<body>
<h1 id="title">To-do list</h1>
<div id="task_area">
</div>
<input type="text" id="putin" />
<button id="add">add</button>
<button id="clear">Clear completed tasks</button>
<script>
var tasks = document.getElementById("task_area")
var new_task = document.getElementById("add")
var clear = document.getElementById("clear")
new_task.addEventListener("click", function () {
var putin = document.getElementById("putin")
var input = document.createElement('input')
input.type = "checkbox"
var label = document.createElement('label')
label.appendChild(document.createTextNode(putin.value))
task_area.appendChild(input)
task_area.appendChild(label)
//document.getElementById("task_area").innerHTML = putin.value
})
clear.addEventListener("click", function () {
for (i = 0; i < task_area.children.length; i++) {
if (task_area.children[i].checked === true) {
tasks.children[i].nextSibling.remove();
tasks.children[i].remove();
}
}
})
</script>
</body>
Please let me know if any concern.

Categories

Resources