I have a range of dynamic data that shows data from a dropdown list.
The data :
Data Sample :
I tried using script from this post.
function onEdit(e) {
const sheetName = "MONTHLY OVERVIEW";
const expectedRange = "C10:G1000";
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName) return;
const r = sheet.getRange(expectedRange);
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
if (range.rowStart < startRow || range.rowEnd > endRow || range.columnStart < startCol || range.columnEnd > endCol) return;
r.setBorder(null, false, false, false, false, false);
const last = r.getDisplayValues().findIndex(rr => !rr.join(""));
sheet.getRange(startRow, startCol, last != -1 ? last : r.getNumRows(), endCol - startCol + 1).setBorder(true, true, true, true, true, false);
}
But the border will only appear when I edit the cell below the last dynamic data.
Is there any way to make the border automatically added according to the dynamic data, with an extra empty row before the border on the bottom, like shown on the image above?
I have a range of dynamic data that shows data from a dropdown list.
The data :
Data Sample : https://docs.google/spreadsheets/d/1NGsqFGFt_hDHUT6LscKTKqh5hxQVU1DJ_Ixbuuvrkdc/edit?usp=sharing
I tried using script from this post.
function onEdit(e) {
const sheetName = "MONTHLY OVERVIEW";
const expectedRange = "C10:G1000";
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName) return;
const r = sheet.getRange(expectedRange);
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
if (range.rowStart < startRow || range.rowEnd > endRow || range.columnStart < startCol || range.columnEnd > endCol) return;
r.setBorder(null, false, false, false, false, false);
const last = r.getDisplayValues().findIndex(rr => !rr.join(""));
sheet.getRange(startRow, startCol, last != -1 ? last : r.getNumRows(), endCol - startCol + 1).setBorder(true, true, true, true, true, false);
}
But the border will only appear when I edit the cell below the last dynamic data.
Is there any way to make the border automatically added according to the dynamic data, with an extra empty row before the border on the bottom, like shown on the image above?
Instead of using onEdit
, use onChange
with installable triggers. The onEdit
trigger does not detect changes made through dropdown lists unless you manually edit the data.
Updated Code:
function handleEdit() {
const sheetName = "MONTHLY OVERVIEW";
const expectedRange = "C10:G1000";
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (!sheet) return;
const r = sheet.getRange(expectedRange);
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
const range = sheet.getActiveRange();
if (range.rowStart < startRow || range.rowEnd > endRow || range.columnStart < startCol || range.columnEnd > endCol) return;
r.setBorder(false, false, false, false, false, false);
// Find the last non-empty row
const values = r.getDisplayValues();
let lastRow = 0;
for (let i = 0; i < values.length; i++) {
if (values[i].some(cell => cell.trim() !== "")) {
lastRow = i + 1;
}
}
// Add borders around dynamic data and an extra empty row below
if (lastRow > 0) {
const borderRange = sheet.getRange(startRow, startCol, lastRow + 1, endCol - startCol + 1);
borderRange.setBorder(true, true, true, true, true, false);
}
}
Sample Output:
1.) Open your Apps Script project.
2.) On the left, click Triggers alarm
.
3.) At the bottom right, click Add Trigger.
4.) Select the On Change.
5.) Click Save
.
Once set up, the onChange trigger
will automatically respond to changes in the spreadsheet, including updates via dropdowns or formulas.
Reference:
Event-driven triggers
Installable Triggers
If data has been updated while document was closed. You could use a different trigger such as onOpen, or just update it with specific interval.
Info on those triggers
You want to query/filter data based on a "Month" dropdown, and then set borders around the entire data output.
Consider this answer:
Logic
QUERY
populates the output based on the dropdown date in cell C4.Col1 >= date '"&TEXT(C4,"yyyy-mm-dd")&"' and Col1 <= date '"&TEXT(eomonth(C4,0),"yyyy-mm-dd")&"'
onEdit
script clears any existing borders
sheet.getRange(firstRowOfData,startCol,rows-firstRowOfData+1,5).setBorder(null, false, false, false, false, false)
var targetRange = sheet.getRange(firstRowOfData,startCol,lastRow-firstRowOfData+1,5)
targetRange.setBorder(true, true, true, true, true, true);
SCRIPT
function onEdit(e) {
Logger.log(JSON.stringify(e)) // DEBUG
const sheetName = "answer"; // This is from your provided Spreadsheet.
const expectedRange = "C4"; // This is from your question.
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName) return;
const r = sheet.getRange(expectedRange);
// define the parameters
const startRow = 4
const startCol = 3
var firstRowOfData = 6
Logger.log("DEBUG: rowStart = "+range.rowStart+"startRow = "+startRow)
Logger.log("DEBUG: Column Start = "+range.columnStart+"\nstartCol = "+startCol)
if (range.rowStart !== startRow || range.columnStart !== startCol) {
// Logger.log("DEBUG: IF failed")
return;
}
// get the last row of data
var lastRow = sheet.getLastRow()
// get the maxmumum number of rows
var rows = sheet.getMaxRows()
Logger.log("DEBUG: the last row of data = "+lastRow+"\nthe number of rows = "+rows)
// clear any existing borders
sheet.getRange(firstRowOfData,startCol,rows-firstRowOfData+1,5).setBorder(null, false, false, false, false, false);
// create new border
var targetRange = sheet.getRange(firstRowOfData,startCol,lastRow-firstRowOfData+1,5)
Logger.log("DEBUG: the target range = "+targetRange.getA1Notation())
targetRange.setBorder(true, true, true, true, true, true);
}
QUERY
Enter this formula in cell C6 (assuming the raw data is on sheet name = "Sheet1":
=query(
{Sheet1!A1:E},
"select Col1,Col2,Col3, Col4, Col5
where Col1 is not null and
Col1 >= date '"&TEXT(C4,"yyyy-mm-dd")&"' and
Col1 <= date '"&TEXT(eomonth(C4,0),"yyyy-mm-dd")&"' ",1
)
Data Validation (dropdowns)
SAMPLE - Raw data
SAMPLE - September Results
SAMPLE - October results
Here's another solution. It's a little known fact that Google Sheets supports conditional formatting rules for borders when those rules have been imported from an Excel file.
I took your sample spreadsheet and exported it as an Excel file. Once opened in Excel, I created the following three CF rules.
First row: For C10:G10, I applied the solid top, left, and right border along with the dotted line for the bottom using custom formula:
=COUNTA($C10:$G10)>0.
Mid rows: For C11:G100, I used the solid left and right borders plus the dotted top and bottom border using custom formula:
=COUNTA($C11:$G11)>0.
Bottom row: For the same range, I used the custom formula:
=(COUNTA($C11:$G11)=0)*(COUNTA($C10:$G10)>0)>0
This identified the bottom row, for which I made the top border dotted and the left, right, and bottom borders solid.
Once those were created, I downloaded the .xlsx file and imported it into Sheets.
The result, instant dynamic borders without the use of Apps Script.