0

Now I understand that the question is deeper, and is connected with the tracking of events.

In Excel, I use this code:

If Not Intersect(Target, Sh.Range("$A$1:$A$300")) Is Nothing sub_do_something()

Here, Target - the address of the selected cell, Intersect determines whether the cell belongs to the specified range.

I use it in the system for filling and calculating the costing of the project.

The user clicks a row in a specific section of the calculation template. The script determines the address of the selected cell and switches the user to a specific sheet of the directory. Next, the user clicks on the desired line of the directory, the script copies a certain range of cells in the line and returns the user back to the calculation. When this happens, the copied data is inserted into a range of cells, starting with the selected one.

Thus, the creating a calculation, in which there can be more than 100 positions, is greatly simplified.

In Excel, everything works fine, but soon I plan to transfer this project to a cloud-based service, and Google Sheets is the best option.

Alas, only some events can be tracked in GAS, for example, using onOpen or onEdit triggers. Excel has much more tracked events.

After a search on the StackOverflow, I found several similar issues related to tracing events, for example, How to find where user's cursor is in Document-bound script, Can we implement some code that fires upon selecting something in google document?, Google app script monitor spreadsheet selected ranges.

From the answers to these questions, it is clear that in GAS there is no such simple solution as Intersect(Target, Range) in Excel. The last example uses the side menu, running a script from it that queries the sheet 5 times per second, and displays the address of the active cell in the "data" field.

Unfortunately, this code does not work for me. In the debugger, the getActiveRange() function works fine, but this code does not work:

$(document).ready(() => {
   setInterval(()=>{
   google.script.run.withSuccessHandler(log).getActiveRange();
   },200)    
  })       
  log(e) => {
    $('#data').val(e)
  }

Question.

If someone did something similar, please share your experience. Or tell me why this example does not work. If he can be reanimated, I will adapt him to solve my task.

Rubén
  • 34,714
  • 9
  • 70
  • 166
Boris Baublys
  • 952
  • 10
  • 22
  • From what I understand, you want to check if a cell is within a specified range? Have a look at [this post](https://stackoverflow.com/q/12583187/7498443). – CalamitousCode Jan 08 '19 at 18:53
  • @s1c0j1 Thank you, but this is not exactly what you need. With the onEdit trigger, everything is clear, but in my example there is no cell [change event](https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.change) The user clicks on the cell and a [SelectionChange Event](https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.selectionchange) occurs. This is from Excel, but the logic is the same. This is an event when the value of the cell has not changed yet, but the cell is already selected I need to catch it. – Boris Baublys Jan 08 '19 at 20:55
  • Right. Apologies.Yes, it's unfortunate, but it doesn't seem like Google Apps Script can track user selections. Also, the most often you can trigger a script is once a minute. You could get around this by making sure your function runs for at least a minute and triggering it every minute.. – CalamitousCode Jan 08 '19 at 20:57
  • @s1c0j1 In Excel, the user now spends about 1 second to select one item from the Directory and transfer it to Calculation. Thus, it takes no more than a few minutes to complete the Calculation. If this is done at a frequency of 1 time per minute, then it will take a couple of hours. In [Google app script monitor spreadsheet selected ranges](https://stackoverflow.com/questions/47919639/google-app-script-monitor-spreadsheet-selected-ranges) the sheet is queryed every 200 ms. That would be good, but this example does not work for me. And you tried it, does this code work for you? – Boris Baublys Jan 08 '19 at 21:17
  • The range can be checked several times in one function, so it won't be just once a minute. I have not tried the code because I don't believe it would work in the Google Apps Script editor. It looks unfamiliar and I think it's another way of calling a script from outside the editor. I can't be sure, though. – CalamitousCode Jan 08 '19 at 21:29
  • 1. You can use hyperlinks to switch over to directory sheets. 2.Try using a Macro to copy/paste. Sheets is not excel. Although similar on the outside, Both are built differently from the ground up. You need to stop trying to replicate excel and innovate your flow from the ground up for the current environment. – TheMaster Jan 08 '19 at 21:41
  • "This code doesn't work" doesn't actually describe anything useful. The polling methodology in https://stackoverflow.com/questions/47919639/google-app-script-monitor-spreadsheet-selected-ranges, plus the use of your own range intersection code, and the `setActiveSheet` functions should combine well to solve your task – tehhowch Jan 08 '19 at 21:46
  • @tehhowch "This code doesn't work" doesn't actually describe anything useful". Right. Apologies. I meant that when I click on a sheet in different cells, in the "date" field is empty. In the debugger, I see that the function getActiveRange() works well, but I don’t know how to view the DOM drawing. You tried code from [Google app script monitor spreadsheet selected ranges](https://stackoverflow.com/questions/47919639/google-app-script-monitor-spreadsheet-selected-ranges), does this code work for you? – Boris Baublys Jan 08 '19 at 23:18
  • @TheMaster yes, Excel and Sheets are completely different and direct copying of code from one to another is impossible. However, as a result of using algorithms at the junction of these two worlds, interesting solutions are sometimes obtained. The vba code automatically determines which Calculation section the user has clicked and switches it to the sheet corresponding to this section. The hyperlink solution forces the user to make this choice. He may be mistaken, and he will spend extra time on this process. Using hyperlinks will reduce user productivity and increase the number of errors. – Boris Baublys Jan 08 '19 at 23:21
  • I don't get what you mean by "View the DOM drawing" - do you mean how to open the sidebar menu? You probably need to review a whole slew of Apps Script UI documentation then: https://developers.google.com/apps-script/guides/html/#serve_html_as_a_google_docs_sheets_slides_or_forms_user_interface It works for me: https://i.imgur.com/iVJUBeK.png (I did change the html file to remove the jquery dependency and so i could control the polling) – tehhowch Jan 09 '19 at 00:08
  • @tehhowch Oh, you did it! It would be interesting to look at your code. Thanks for the advice, this section of documentation has bypassed me, I will study it. how to open the sidebar menu I understand. – Boris Baublys Jan 09 '19 at 01:47

3 Answers3

2

I worked on a similar project and here's the solution:

function onSelectionChange(e)
{
  var ss = e.source;
  var Sh = ss.getActiveSheet();
  var range = Sh.getRange("A1:A300");
  var target = e.source.getActiveRange();

  //check for intersection
  if(RangeIntersects(target, range))
  {
    Logger.log("Changed Row: " + target.getRow() + "\nValue: " + target.getValue());
  }
}

//returns true if target intersects with the predefined range
function RangeIntersects(target, range)
{
  return (target.getLastRow() >= range.getRow()) && (range.getLastRow() >= target.getRow()) && (target.getLastColumn() >= range.getColumn()) && (range.getLastColumn() >= target.getColumn());
}

Peter Kalu
  • 36
  • 6
  • Thank you, but an error occurs: "ReferenceError: Sh is not defined at onEdit". If you add var ss = e.source; var Sh = ss.getActiveSheet (), it works, but only when editing a cell. If you only select a cell, but don’t edit it, then it doesn’t work. – Boris Baublys May 04 '20 at 08:02
  • @BorisBaublys The post has been modified. If you desire to do something when a cell is selected, then change the onEdit(e) function to onSelectionChange(e). – Peter Kalu May 05 '20 at 08:36
  • Yes, onSelectionChange solves the issue, thanks! If you correct onEdit to onSelectionChange in your answer, then I can take him as a decision. – Boris Baublys May 05 '20 at 12:39
  • 1
    @BorisBaublys, it's been modified. Please mark solution as as accepted if it solves the problem. – Peter Kalu May 19 '20 at 22:08
0

Here's an idea. I can't quite get it to work though.

Maybe someone else can give a better answer.

Also, having functions running 24/7 is not possible with GAS, I think, as there are limits to the total run-time. You may wish to add a code-guard that exits the script if the last update time is longer than 10 minutes ago or something.

function checkSelection() {

    var spreadsheet = SpreadsheetApp.getActive();
    var targetRange = spreadsheet.getRange('activate');
    // Change your named ranged name here

    var tCol = targetRange.getColumn();
    var tLastCol = targetRange.getLastColumn();
    var tRow = targetRange.getRow();
    var tLastRow = targetRange.getLastRow();

    var num = 0;

    for (num; num < 115; ++num) {
    // Repeats the code below 100 times

        var range = spreadsheet.getActiveRange();
        var row = range.getRow();
        var col = range.getColumn();

        if (col >= tCol && col <= tLastCol && row >= tRow && row <= tLastRow) {
            range.setBackground('#000000');
            // Change the code in this block to your code.
        }

        SpreadsheetApp.flush();
        Utilities.sleep(500);
        // Waits half a second before repeating
    }
}

115 repetitions * 500ms wait seems to run for almost a minute, then the trigger will fire the whole function again.

Screenshot of executions

CalamitousCode
  • 1,324
  • 1
  • 14
  • 21
0

Intersection of two Ranges

You can use this to calculate intersection of two ranges. It requires an object in the form of: {rg1:'A1Notation String',rg2:'A1Notation String'}

function calculateIntersection1(rgObj) {
  var iObj={};
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getActiveSheet();
  var rg1=sh.getRange(rgObj.rg1);
  var rg2=sh.getRange(rgObj.rg2);
  var iObj={rg1colst:rg1.getColumn(),rg1colen:rg1.getColumn()+rg1.getWidth()-1,rg1rowst:rg1.getRow(),rg1rowen:rg1.getRow()+rg1.getHeight()-1,rg2colst:rg2.getColumn(),rg2colen:rg2.getColumn()+rg2.getWidth()-1,rg2rowst:rg2.getRow(),rg2rowen:rg2.getRow()+rg2.getHeight()-1};
  if(iObj.rg1colst>iObj.rg2colen || iObj.rg1colen<iObj.rg2colst || iObj.rg1rowst>iObj.rg2rowen || iObj.rg1rowen<iObj.rg2rowst || iObj.rg2colst>iObj.rg1colen || iObj.rg2colen<iObj.rg1colst || iObj.rg2rowst>iObj.rg1rowen || iObj.rg2rowen<iObj.rg1rowst) {
    return '<h1>No intersecting cells</h1>';
  }else{
    var vA1=rg1.getValues();
    var v1=[];
    var vA2=rg2.getValues();
    var v2=[];
    for(var i=0;i<vA1.length;i++){
      for(var j=0;j<vA1[i].length;j++){
        var s=Utilities.formatString('(%s,%s)', iObj.rg1rowst+i,iObj.rg1colst+j);
        v1.push(s);
      }
    }
    for(var i=0;i<vA2.length;i++){
      for(var j=0;j<vA2[i].length;j++){
        var s=Utilities.formatString('(%s,%s)', iObj.rg2rowst+i,iObj.rg2colst+j);
        v2.push(s);
      }
    }
    var oA=[];
    for(var i=0;i<v1.length;i++){
      var idx=v2.indexOf(v1[i]);
      if(idx>-1){
        oA.push(v2[idx]);
      }
    }
    return Utilities.formatString('Intersecting Cells: %s', oA.join(', '));
  }
}

It either returns the string "No Intersecting Cells" or a string identifying the intersecting cells in (row, column) format.

Cooper
  • 59,616
  • 6
  • 23
  • 54