Google 試算表:取得目前作用中的儲存格範圍 getActiveRange()

 


很多人寫 Apps Script 都從 getRange('A1:B10') 開始,但寫到後來你會發現,真正好用的工具其實是跟著使用者操作走的。

getActiveRange() 讓腳本尊重當下選取,不用硬把範圍寫死,做起來更貼近現場流程。本文先用最短的步驟帶你跑通第一次:開啟編輯器、授權、執行;接著把常見需求拆給你看——單值/批次寫入、字型/底色/邊框、數字與日期格式、資料驗證、註解與超連結。

會特別示範多選範圍的處理方式,還有跟 getCurrentCell()、getActiveSheet() 的搭配。文末我把踩雷清單整理成檢查表:為什麼會拿到 null、為什麼 setValues() 一直報錯、遇到合併儲存格怎麼辦、觸發器該改寫哪段。希望本篇文章能幫助到需要的您。


目錄

{tocify} $title={目錄} 


什麼是「Active Range」?getActiveRange() 的定位

Active Range(使用者目前選取的區域):在試算表 UI 中,你滑鼠拖拉反白的那一塊,就是 Active Range。

getActiveRange():在 GAS 端取得「當下使用者選取」的範圍(Range 物件)。

語法有兩種等價寫法:

const range = SpreadsheetApp.getActiveRange();
// 或
const range = SpreadsheetApp.getActive().getActiveRange();


回傳可能是 null(例如:腳本不是由使用者在該試算表互動觸發;或使用時間驅動觸發器執行,沒有 UI 也就沒有選取)。


什麼時候該用?

需要對「使用者剛選到的儲存格或區域」做事:填值、上色、標註、驗證、建立超連結、快速格式化…

製作側邊欄/選單工具,讓使用者「先選再按鈕」。


最快上手:從 0 到能跑(操作步驟)

1.    開啟試算表 → 工具列點「擴充功能」→「Apps Script」,進到 GAS 編輯器。

2.    新增檔案 Code.gs,貼上下面入門範例。

3.    回到試算表,選一塊範圍,再回 GAS 按「執行」。

4.    首次會跳權限授權,依流程允許。

5.    回到試算表確認結果。

入門範例:在使用者選取區域填入時間戳與作者

function stampSelection() {
  const range = SpreadsheetApp.getActiveRange();
  if (!range) {
    SpreadsheetApp.getUi().alert('沒有選取範圍。請先反白一塊儲存格或區域再執行。');
    return;
  }
  const user = Session.getActiveUser().getEmail() || 'unknown';
  const ts = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm:ss');
  const values = range.getValues().map(row => row.map(() => `${ts} by ${user}`));
  range.setValues(values);
}

基礎用法大全

讀取選取區的值與尺寸

function readActiveRangeInfo() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  Logger.log(`Sheet: ${r.getSheet().getName()}`);
  Logger.log(`Rows: ${r.getNumRows()}, Cols: ${r.getNumColumns()}`);
  Logger.log(r.getValues());
}


寫入資料(單值 / 二維陣列)

function writeHello() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  if (r.getNumRows() === 1 && r.getNumColumns() === 1) {
    r.setValue('Hello');
  } else {
    const data = Array.from({length: r.getNumRows()}, () =>
      Array.from({length: r.getNumColumns()}, () => 'Hello')
    );
    r.setValues(data); // 尺寸必須完全相符
  }
}


基礎格式(字型、顏色、邊框、對齊)

function quickFormat() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  r.setFontFamily('Noto Sans')
   .setFontSize(12)
   .setFontWeight('bold')
   .setHorizontalAlignment('center')
   .setVerticalAlignment('middle')
   .setBackground('#FFF9C4') // 淡黃(提升可讀性)
   .setFontColor('#333333');

  r.setBorder(true, true, true, true, true, true);
}


數字與日期格式

function numberAndDateFormat() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  r.setNumberFormat('[Blue]#,##0;[Red]-#,##0;0'); // 正藍負紅
  r.offset(0, 1).setNumberFormat('yyyy-mm-dd');   // 右側欄設定日期
}


驗證規則(清單、數字區間、必填)

function addValidations() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;

  const ruleList = SpreadsheetApp.newDataValidation()
    .requireValueInList(['高', '中', '低'], true)
    .setAllowInvalid(false)
    .setHelpText('請從下拉選單選擇:高/中/低')
    .build();

  const ruleNumber = SpreadsheetApp.newDataValidation()
    .requireNumberBetween(0, 100)
    .setAllowInvalid(false)
    .setHelpText('0~100 的數字')
    .build();

  r.setDataValidation(ruleList);
  r.offset(0, 1, r.getNumRows(), 1).setDataValidation(ruleNumber);
}


超連結與註解

function addLinksAndNotes() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;

  // 為第一欄加上超連結
  const firstCol = r.offset(0, 0, r.getNumRows(), 1);
  const values = firstCol.getValues().map(row => {
    const text = row[0];
    return [`=HYPERLINK("https://www.google.com/search?q=${encodeURIComponent(text)}","查詢「${text}」")`];
  });
  firstCol.setValues(values);

  // 為整個選取區加上註解
  r.setNote('自動產生的連結,請確認內容是否正確');
}


進階:多選範圍 getActiveRangeList() 與批次處理

使用者按住 Ctrl/⌘ 多選不連續區塊時,getActiveRange() 只能抓到「第一塊」。要一次處理多塊,請用 getActiveRangeList():

function batchPaintMultiSelection() {
  const rangeList = SpreadsheetApp.getActive().getActiveRangeList();
  if (!rangeList) {
    SpreadsheetApp.getUi().alert('沒有多重選取。請按住 Ctrl/⌘ 選取多塊區域再執行。');
    return;
  }
  rangeList.getRanges().forEach(r => {
    r.setBackground('#E3F2FD').setFontWeight('bold');
  });
}


小技巧:

需要同時設定邊框、格式、驗證,建議先把要套的設定封裝成函式,對 rangeList.getRanges() 迴圈呼叫,並搭配 SpreadsheetApp.flush() 在大量操作後一次刷新(見效能章節)。


真實場景 10 例

批次清洗空白

function trimSpaces() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const data = r.getValues().map(row => row.map(v => (typeof v === 'string') ? v.trim() : v));
  r.setValues(data);
}


一鍵標記狀態(完成/待辦)

function toggleDone() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const data = r.getValues().map(row => row.map(v => v === '完成' ? '待辦' : '完成'));
  r.setValues(data);
  r.setBackground('#C8E6C9');
}


快速插入遞增編號

function fillSerial() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const rows = r.getNumRows(), cols = r.getNumColumns();
  let n = 1;
  const serials = Array.from({length: rows}, () => Array.from({length: cols}, () => n++));
  r.setValues(serials);
}


把選取資料轉大寫/小寫/Title Case

function toUpperCaseSel() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const data = r.getValues().map(row => row.map(v => (typeof v === 'string') ? v.toUpperCase() : v));
  r.setValues(data);
}


批次設為千分位、百分比

function thousandAndPercent() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  r.setNumberFormat('#,##0');
  r.offset(0, 1, r.getNumRows(), 1).setNumberFormat('0.00%');
}


依值條件上色(自訂規則)

function colorByValue() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const values = r.getValues();
  for (let i = 0; i < r.getNumRows(); i++) {
    for (let j = 0; j < r.getNumColumns(); j++) {
      const cell = r.getCell(i+1, j+1);
      const v = values[i][j];
      cell.setBackground(v >= 0 ? '#E8F5E9' : '#FFEBEE');
    }
  }
}


將選取區搬運到新工作表

function moveSelectionToNewSheet() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const ss = r.getSheet().getParent();
  const sh = ss.insertSheet('剪下的資料 ' + new Date().toLocaleString());
  sh.getRange(1,1, r.getNumRows(), r.getNumColumns()).setValues(r.getValues());
  r.clearContent();
}


建立資料驗證下拉 + 預設值

function dropdownWithDefault() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  const rule = SpreadsheetApp.newDataValidation()
    .requireValueInList(['新進','在職','離職'], true)
    .setAllowInvalid(false)
    .build();
  r.setDataValidation(rule).setValue('在職');
}


把選取區轉為圖表資料區(加上標題列)

function markAsChartData() {
  const r = SpreadsheetApp.getActiveRange();
  if (!r) return;
  r.offset(-1, 0, 1, r.getNumColumns()).setFontWeight('bold'); // 假設上方一列為標題
  // 後續可用 Charts 服務建立圖表(略)
}


多選範圍批次貼上格式(RangeList)

function pasteFormatToMulti() {
  const list = SpreadsheetApp.getActive().getActiveRangeList();
  if (!list) return;
  const [first, ...rest] = list.getRanges();
  rest.forEach(r => first.copyFormatToRange(r.getSheet(), r.getColumn(), r.getLastColumn(), r.getRow(), r.getLastRow()));
}


錯誤訊息與常見雷點

1.    range 為 null

情境:用時間驅動或 Web App 呼叫,沒有 UI → 沒有選取。

解法:

改在使用者有 UI 的情境執行(自訂選單 / 按鈕)。

或改設計:用固定範圍(sheet.getRange('A1:B10'))或從參數接收。


2.    setValues() 尺寸不符

訊息:The number of rows in the data does not match the number of rows in the range.

解法:二維陣列的列數、欄數 必須 跟 Range 尺寸完全一致。善用:

const rows = r.getNumRows(), cols = r.getNumColumns();
const data = Array.from({length: rows}, () => Array.from({length: cols}, () => 'x'));
r.setValues(data);


3.    合併儲存格導致寫入失敗

setValues() 寫入到有合併的區塊很容易報錯。

解法:

if (r.isPartOfMerge()) {
  r.breakApart(); // 視需求拆開,或重新選取不含合併的區域
}


4.    多選範圍卻用 getActiveRange()

只會抓到第一塊,其他被忽略。

解法:使用 getActiveRangeList(),對 getRanges() 逐一處理。


5.    權限不足 / 第一次授權

第一次執行可能跳「需要授權」。

解法:按流程授權。若是部署給他人使用,請用「編輯器附加元件」或在同域共享,並說明授權步驟。


6.     觸發器中使用 getActiveRange()

簡單觸發器(如 onEdit(e))有 e.range 可用;時間驅動觸發器沒有 UI,getActiveRange() 通常 null。

解法:在 onEdit(e) 用 e.range,在時間排程改用固定 Range 邏輯。


7.    跨試算表誤用

SpreadsheetApp.getActiveRange() 只會取 目前活躍的試算表。

解法:跨檔案請改 SpreadsheetApp.openById(id).getSheetByName(name).getRange(...),不要依賴 Active。


問題集

Q1:為何我執行時 getActiveRange() 是 null?

A:多半因為沒有 UI(時間觸發、API 呼叫)或沒有真正選到範圍。請改 UI 互動場合,或改以固定座標。

Q2:我要處理多塊不連續範圍?

A:使用 getActiveRangeList() → getRanges(),逐一操作。

Q3:setValues() 一直爆錯?

A:檢查二維陣列尺寸是否完全一致;合併儲存格是否已拆開。

Q4:要不要用 getCurrentCell()?

A:只針對單一焦點格時更合適;需要整塊就用 getActiveRange()。

Q5:我想把選到的資料搬去另一個檔案?

A:不要用 Active 去開別檔;直接 openById() 目標檔,再 getSheetByName() + getRange() 寫入。


張貼留言 (0)
較新的 較舊