Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Google apps script 在Google工作表电子表格中跨工作表(选项卡)同步滚动_Google Apps Script_Google Sheets_Scroll_Synchronous - Fatal编程技术网

Google apps script 在Google工作表电子表格中跨工作表(选项卡)同步滚动

Google apps script 在Google工作表电子表格中跨工作表(选项卡)同步滚动,google-apps-script,google-sheets,scroll,synchronous,Google Apps Script,Google Sheets,Scroll,Synchronous,我不熟悉谷歌表单、应用程序脚本和JavaScript,所以很乐意与我分享 我对Google Sheets特别感兴趣,因为它能够控制用户访问特定范围的数据输入 我有一些信息,我想在电子表格中的不同表格中拆分。由于有很多详细信息可以分类,因此我计划将每个类别的信息划分为不同的工作表/选项卡,以便于使用 由于预计会有大量行,我希望能够同步滚动这些分类表,以便于使用。我在Excel上使用宏也做到了这一点,以便了解我打算做什么 基本上,当在活动类别工作表中选择一个单元格(甚至行)时,在所有其他非活动类别工

我不熟悉谷歌表单、应用程序脚本和JavaScript,所以很乐意与我分享

我对Google Sheets特别感兴趣,因为它能够控制用户访问特定范围的数据输入

我有一些信息,我想在电子表格中的不同表格中拆分。由于有很多详细信息可以分类,因此我计划将每个类别的信息划分为不同的工作表/选项卡,以便于使用

由于预计会有大量行,我希望能够同步滚动这些分类表,以便于使用。我在Excel上使用宏也做到了这一点,以便了解我打算做什么

基本上,当在活动类别工作表中选择一个单元格(甚至行)时,在所有其他非活动类别工作表中选择同一行。或者,当用户选择类别工作表时,应检查在先前选择的类别工作表中选择了哪一行,并在新激活的工作表中选择同一行

我正在玩应用程序脚本,但无法设置非活动工作表的选择。类似地,当选择另一个类别工作表时,我无法从以前选择的类别工作表中获取所选范围-getSelection()和getActiveRange()始终返回当前活动工作表的选择

我有什么办法可以做到这一点吗?有没有人可以建议其他解决方案来达到类似的目标?例如,我也在看侧边栏,但这减少了可查看的列数。。。希望尽量减少水平滚动,因为这是在工作表之间拆分数据的主要目的

我将非常感谢任何反馈


提前感谢。

如果我理解正确,您希望通过编程将所有工作表滚动到与活动范围相同的行

如果是这种情况,您只需查看所有工作表,并将相应的行(使用活动范围中的
行索引
)设置为活动范围,使用,如下所示:

function scrollAllSheets() {
  const rowIndex = SpreadsheetApp.getActiveRange().getRow();
  const sheets = SpreadsheetApp.getActive().getSheets();
  sheets.forEach(sheet => {
    const row = sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn());
    sheet.setActiveRange(row);
    //SpreadsheetApp.flush();
  });
}
如果希望在单击新单元格时运行脚本,请使用触发器。为此,只需将函数名更改为
onSelectionChange
,并可选地使用(参数
e
):

注:
  • 我认为没有必要,但为了确保所有工作表的电子表格选择都已更新,您可能会发现这很有用。如果它不工作,请从上面的代码中取消注释它
  • 此处不选择使用,因为它指的是同一工作表中的范围列表

为了扩展Iamblichus的回答,请注意,有5张表1-5,其中只有表1-3需要同步

我研究了两个解决方案,一个是通过自定义菜单同步表,另一个是选择更改。请注意以下代码:

    // all sheets
var syncSheet1 = "Sheet1"
var syncSheet2 = "Sheet2"
var syncSheet3 = "Sheet3"
var syncSheet4 = "Sheet4"
var syncSheet5 = "Sheet5"

// array of sync sheets
var syncedSheets = new Array (syncSheet1, syncSheet2, syncSheet3);

function onOpen(e) {
  // Add a custom menu to the spreadsheet.
  SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
      .createMenu("Sync Sheets")
      .addItem("Sync Sheets","scrollAllSheets")
      .addToUi();

  SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
      .createMenu("Goto 10,5")
      .addItem("Goto 10,5","goto10_5")
      .addToUi();

  const prop = PropertiesService.getScriptProperties();
  const sheetName = e.range.getSheet().getSheetName();
  const sheetRowIndex = e.range.getRow();
  prop.setProperty("previousSheet", sheetName);
  prop.setProperty("previousSheetRow", sheetRowIndex);
}

function goto10_5(){
  const row = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(10,5);
  SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().setActiveRange(row);
  // SpreadsheetApp.flush();
}

function scrollAllSheets() {
  const currentRange = SpreadsheetApp.getActiveRange();
  const rowIndex = SpreadsheetApp.getActiveRange().getRow();
  const currentSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName();
  if (syncedSheets.indexOf(currentSheet) !=  -1){
    const sheets = SpreadsheetApp.getActive().getSheets();
    sheets.forEach(sheet => {
      if (syncedSheets.indexOf(sheet.getSheetName()) != -1){
        const row = sheet.getRange(rowIndex, 1);
        row.setValue(sheet.getSheetName() + " ;row: " + rowIndex);
        sheet.setActiveRange(row);
        SpreadsheetApp.flush();
      }
    });
    SpreadsheetApp.getActiveSpreadsheet().getSheetByName(currentSheet).setActiveRange(currentRange);
  } else {
    currentRange.setValue(currentSheet + " is not a synced sheet");
  }
}

function onSelectionChange(e) {
  const prop = PropertiesService.getScriptProperties();
  const previousSheet = prop.getProperty("previousSheet");
  const previousSheetRow = prop.getProperty("previousSheetRow");
  const range = e.range;
  const a1Notation = range.getA1Notation();
  const rowIndex = range.getRow();
  const sheet = range.getSheet();
  const sheetName = sheet.getSheetName();
  if (sheetName != previousSheet) {
    if (syncedSheets.indexOf(previousSheet) != -1 && syncedSheets.indexOf(sheetName) != -1){
      if (rowIndex != parseInt(previousSheetRow)){
        range.setValue(previousSheet + " row: " + previousSheetRow + "; dif " + rowIndex + " " + sheetName);
        var row = sheet.getRange(parseInt(previousSheetRow),2);
        row.setValue(sheetName);
        sheet.setActiveRange(row);
        // row.activate();
        SpreadsheetApp.flush();
      } else {
        range.setValue("same " + rowIndex + " " + sheetName);
      }
    } else {
      range.setValue("from not synced sheet: " + previousSheet);
    }
      
  } else {
      range.setValue(a1Notation);
  }
  prop.setProperty("previousSheet", sheetName);
  prop.setProperty("previousSheetRow",rowIndex);
  SpreadsheetApp.flush();
}

非常感谢您的帮助。我将让最终用户选择要实施的解决方案。

非常感谢您的反馈,Iamblichus,很抱歉延迟响应,因为我是应用程序新手,所以我花了一段时间才理解并运行应用程序脚本。我稍微修改了脚本,以便在完成所有其他工作表后选择初始选择,并且需要yes flush()才能获得所需的效果。如果没有flush,setActiveRange不会在循环中运行。虽然这样做有效,但如果在每个单元格选择上运行,可能会非常消耗资源。我们有没有办法将其限制在行更改时?另外,如果我们只在选择不同的工作表时循环所有工作表,不是会更有效吗?我见过一些代码,我们可以检测到以前选择的工作表是什么。然后我们可以在该工作表中找到所选行并将其应用于新选择的工作表吗?我无法获取非活动工作表的选定行。只有在选择新工作表时,它才能从上一个工作表中获取行并循环。@Shalin
onSelectionChange
event对象不包含关于上一行和工作表是什么的信息,只包含当前行和工作表的信息(在选择更改后),因此无法直接检索该行和工作表。您可以通过存储上一个选中的行,并在函数启动时将其与当前行进行比较,但我不确定这是否值得(存储和检索属性也需要时间)。@Shalin如果您认为这样(使用
PropertiesService
)比循环所有工作表更有效(如果你有很多工作表,很可能是),我建议在我的答案中添加这个选项。你怎么看?嗨,Iamblichus,我一直在做同样的工作,并将在一个单独的答案中发布代码。我提供了两个解决方案,一个是通过菜单(我认为这是最节省资源的,只需要用户点击两次)另一个是通过PropertiesService,这样只有在工作表发生变化时才会在工作表中循环。考虑到您最初的问题(如何在选择新单元格时自动滚动不同的工作表),我认为这个答案与我的答案没有任何关系,特别是通过菜单启动一个功能。使用
PropertiesService
存储/检索以前选择的行/表是一个不错的添加,但在我看来不值得一个新的答案。因此,我认为它更适合作为对我的答案的编辑。注意。我在stackoverflow上发布了一段时间了。将保持注意。最后,我相信f通过菜单启用该功能是最节省资源的解决方案。再次感谢您宝贵的帮助!
    // all sheets
var syncSheet1 = "Sheet1"
var syncSheet2 = "Sheet2"
var syncSheet3 = "Sheet3"
var syncSheet4 = "Sheet4"
var syncSheet5 = "Sheet5"

// array of sync sheets
var syncedSheets = new Array (syncSheet1, syncSheet2, syncSheet3);

function onOpen(e) {
  // Add a custom menu to the spreadsheet.
  SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
      .createMenu("Sync Sheets")
      .addItem("Sync Sheets","scrollAllSheets")
      .addToUi();

  SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
      .createMenu("Goto 10,5")
      .addItem("Goto 10,5","goto10_5")
      .addToUi();

  const prop = PropertiesService.getScriptProperties();
  const sheetName = e.range.getSheet().getSheetName();
  const sheetRowIndex = e.range.getRow();
  prop.setProperty("previousSheet", sheetName);
  prop.setProperty("previousSheetRow", sheetRowIndex);
}

function goto10_5(){
  const row = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(10,5);
  SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().setActiveRange(row);
  // SpreadsheetApp.flush();
}

function scrollAllSheets() {
  const currentRange = SpreadsheetApp.getActiveRange();
  const rowIndex = SpreadsheetApp.getActiveRange().getRow();
  const currentSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName();
  if (syncedSheets.indexOf(currentSheet) !=  -1){
    const sheets = SpreadsheetApp.getActive().getSheets();
    sheets.forEach(sheet => {
      if (syncedSheets.indexOf(sheet.getSheetName()) != -1){
        const row = sheet.getRange(rowIndex, 1);
        row.setValue(sheet.getSheetName() + " ;row: " + rowIndex);
        sheet.setActiveRange(row);
        SpreadsheetApp.flush();
      }
    });
    SpreadsheetApp.getActiveSpreadsheet().getSheetByName(currentSheet).setActiveRange(currentRange);
  } else {
    currentRange.setValue(currentSheet + " is not a synced sheet");
  }
}

function onSelectionChange(e) {
  const prop = PropertiesService.getScriptProperties();
  const previousSheet = prop.getProperty("previousSheet");
  const previousSheetRow = prop.getProperty("previousSheetRow");
  const range = e.range;
  const a1Notation = range.getA1Notation();
  const rowIndex = range.getRow();
  const sheet = range.getSheet();
  const sheetName = sheet.getSheetName();
  if (sheetName != previousSheet) {
    if (syncedSheets.indexOf(previousSheet) != -1 && syncedSheets.indexOf(sheetName) != -1){
      if (rowIndex != parseInt(previousSheetRow)){
        range.setValue(previousSheet + " row: " + previousSheetRow + "; dif " + rowIndex + " " + sheetName);
        var row = sheet.getRange(parseInt(previousSheetRow),2);
        row.setValue(sheetName);
        sheet.setActiveRange(row);
        // row.activate();
        SpreadsheetApp.flush();
      } else {
        range.setValue("same " + rowIndex + " " + sheetName);
      }
    } else {
      range.setValue("from not synced sheet: " + previousSheet);
    }
      
  } else {
      range.setValue(a1Notation);
  }
  prop.setProperty("previousSheet", sheetName);
  prop.setProperty("previousSheetRow",rowIndex);
  SpreadsheetApp.flush();
}