25

I have long used an Applescript triggered from a keyboard shortcut to dismiss notifications on MacOS. It worked by simulating a click of the "close" button of every open Notification Center window.

Since upgrading to Big Sur (currently 11.0.1), notifications no longer have a close button. Instead, when you hover over them with the mouse, an "X" button will appear. For a person who has difficulty clicking precisely (I have a slight hand tremor), using the mouse/trackpad is difficult.

Anyone have suggestions on how to restore that functionality so I can just keep my hands on the keyboard?

My Settings

I am using Automator to test this.

In System Preferences > Notifications, I selected Automator:

  • Allow Notifications is true.
  • Automator alert style: is Alerts.
  • Notification grouping is automatic.

Testing

  • I opened Automator and created a new workflow.
  • I added three instances of the Display Notification action.
  • I gave them Title: values of test 1, test 2, and test 3.
  • And Subtitle: values of subtitle 1, subtitle 2, and subtitle 3.
  • And Message: values of message 1, message 2, and message 3.
  • I ran the workflow using ⌘+R.
  • In the upper-right corner of my screen, I see a notification from Automator stacked over two others. Only notification test 3 is fully visible and it has the notation "2 more notifications" at the bottom.

@AndrewJanian 's Script

  • Again, thank you, Andrew for providing the script!
  • I added the line #!/usr/bin/osascript to the start of the script and saved it as andrew.applescript.
  • I made the file executable. (chmod 755 andrew.applescript).
  • I ran it in a terminal window (./andrew.applescript)
  • The result of running is:
    • the grouped notifications become ungrouped (so I see test 3, test 2, and test 1 stacked top to bottom in the notification area
    • the terminal displays the following error
      ./andrew.applescript:370:377: execution error: System Events got an error: Can’t get action "Name:Clear All
      Target:0x60000116ff20
      Selector:_internal_handleCustomActionWithUiAction:" of group 1 of UI element 1 of scroll area 1 of window "Notification Center" of application process "NotificationCenter". (-1728)
      

I'll try messing around with it to see if I can make it work with modifications.

Note: If I run the script again, starting from the ungrouped state:

  • it dismisses the top-most notification (test 3)
  • and it displays the following in the terminal:
    action Name:Close
    Target:0x60000116ff20
    Selector:_internal_handleCustomActionWithUiAction: of group 1 of UI element 1 of scroll area 1 of window Notification Center of application process NotificationCenter
    
5
  • 1
    If notification is set to Banner type then it will be dismissed automatically after a while, no interaction is needed. Commented Dec 4, 2020 at 14:34
  • 3
    Thanks, @AivarPaalberg. I really want the notifications to stick around until I dismiss them. I just want to be able to dismiss them without using the mouse. [Instead of the old 'Close' button, now there is a tiny X to click on—it's almost like they wanted to make it as small as the "close" button for mobile ads. :-) Commented Dec 9, 2020 at 7:10
  • You can dismiss notifications without aiming at tiny X if you happen to have trackpad. While hovering anywhere over notification two finger swipe from left to right should dismiss it. From productivity perspective I personally don't believe that on desktop notifications and immediate interaction with them is good workflow. Commented Dec 9, 2020 at 10:22
  • 1
    Thank you, @AivarPaalberg. That certainly helps, but I agree it's less than ideal to have to move my mouse cursor up to that corner just to dismiss the notifications. I prefer my old workflow of running a command (okay, pressing a key combo) to make them go away while keeping my focus on whatever was interrupted. Commented Dec 9, 2020 at 19:57
  • And the cross is really very tiny. So tiny that I just noticed it... There is a UI issue here, Apple.
    – Snicolas
    Commented Mar 18, 2021 at 19:57

11 Answers 11

13

Update

I'm posting a most robust version that evolved from my initial post. This seems to do a reasonable job of closing all windows, but it's sometimes slow to execute. As I've stated in the comments, I don't know much about AppleScript, so perhaps someone who knows what they're doing can tell us how to improve the performance.

activate application "NotificationCenter"
tell application "System Events"
    tell process "Notification Center"
        repeat
            
            try
                set theWindow to group 1 of UI element 1 of scroll area 1 of window "Notification Center"
            on error
                exit repeat
            end try
            
            try
                set theActions to actions of theWindow
                
                # Try to close the whole group first. If that fails, close individual windows.
                repeat with theAction in theActions
                    if description of theAction is "Clear All" then
                        set closed to true
                        tell theWindow
                            perform theAction
                        end tell
                        exit repeat
                    end if
                end repeat
                
                repeat with theAction in theActions
                    if description of theAction is "Close" then
                        set closed to true
                        tell theWindow
                            perform theAction
                        end tell
                        exit repeat
                    end if
                end repeat
                
            end try
        end repeat
    end tell
end tell

Original

Yes, thank you for posting the script, @AndrewJanian.

I received an error similar to @ColinFraizer's when using it as is. I think the issue is the inner repeat over the actions: it's trying to access actions in some cases after the window is closed. A minor tweak fixes this issue for me:

activate application "NotificationCenter"
tell application "System Events"
    tell process "Notification Center"
        set theWindow to group 1 of UI element 1 of scroll area 1 of window "Notification Center"
        # click theWindow
        set theActions to actions of theWindow
        repeat with theAction in theActions
            if description of theAction is "Close" then
                tell theWindow
                    perform theAction
                end tell
                exit repeat
            end if
        end repeat
    end tell
end tell
9
  • Does it work on big sur ? I'm getting System Events got an error: Can’t get process "Notification Center" here. Commented Mar 6, 2021 at 17:26
  • When I run this script in the Script Editor on macOS Big Sur 11.2 it dismisses the topmost notification only, but not all notifications.
    – Employee
    Commented Mar 10, 2021 at 0:00
  • @AmadeuCavalcanteFilho Yes, I run it on Big Sur. I'm a total AppleScript newbie, so I'm afraid I don't know what's causing that particular error.
    – lunzwell
    Commented Mar 12, 2021 at 4:13
  • @Employee Yes! I will update the post to add a more recent version that I have wrangled into a more robust form. The updated version does a pretty reasonable job of clearing all notifications for me, although it seems slow. I don't know the first thing about writing good AppleScript, so perhaps someone who knows what they're doing can improve on this.
    – lunzwell
    Commented Mar 12, 2021 at 4:15
  • 2
    @incandescentman I have the same problem after upgrading. The way to access the Window has changed, it seems, but try replacing the existing set theWindow line with set theWindow to group 1 of UI element 1 of scroll area 1 of UI element 1 of window "Notification Center"
    – lunzwell
    Commented Nov 2, 2022 at 13:18
6

I have an AppleScript that works. I found the elements using UIBrowser. Caveat is that the notification must have a "Close" action. All of the notifications I have encountered have that action.

activate application "NotificationCenter"
tell application "System Events"
    tell process "Notification Center"
        set theWindow to group 1 of UI element 1 of scroll area 1 of window "Notification Center"
        click theWindow
        set theActions to actions of theWindow
        repeat with theAction in theActions
            if description of theAction is "Close" then
                tell theWindow
                    perform theAction
                end tell
            end if
        end repeat
    end tell
end tell
1
  • 3
    Thanks, @Andrew. Sadly, this doesn't work for me on Big Sur. I'd love to explain more, but the comment is character limited and won't let me add line breaks, making it very annoying. I'm going to edit my question to give the context. Commented Dec 9, 2020 at 7:07
4

I found an Automator JavaScript action which works:

function run(input, parameters) {

  const notNull = (val) => {
    return val !== null && val !== undefined;
  }

  const appName = "";
  const verbose = true;

  const CLEAR_ALL_ACTION = "Clear All";
  const CLOSE_ACTION = "Close";

  const hasAppName = notNull(appName) && appName !== "";
  const appNameForLog = hasAppName ? (" " + appName) : "";

  const log = (message, ...optionalParams) => {
    console.log("[close_notifications] " + message, optionalParams);
  }

  const logVerbose = (message) => {
    if (verbose) {
      log(message);
    }
  }

  const findCloseAction = (group, closedCount) => {
    try {
      let clearAllAction;
      let closeAction;
      for (let action of group.actions()) {
        if (action.description() === CLEAR_ALL_ACTION) {
          clearAllAction = action;
          break;
        } else if (action.description() === CLOSE_ACTION) {
          closeAction = action;
        }
      }
      if (notNull(clearAllAction)) {
        return clearAllAction;
      } else if (notNull(closeAction)) {
        return closeAction;
      }
    } catch (err) {
      logVerbose(`${closedCount}: Caught error while searching for close action, window is probably closed.`);
      logVerbose(err);
      return null;
    }
    log("No close action found for notification");
    return null;
  }

  const notificationGroupMatches = (group) => {
    if (!hasAppName) {
      return true;
    }

    logVerbose(`Checking UI elements of group...`);
    try {
      for (let elem of group.uiElements()) {
        if (hasAppName && elem.role() === "AXStaticText" && elem.value().toLowerCase() === appName.toLowerCase()) {
          return true;
        }
      }
    } catch (err) {
      logVerbose(`Caught error while checking window, window is probably closed.`);
      logVerbose(err);
    }
    return false;
  }

  const closeNextGroup = (groups, closedCount) => {
    for (let group of groups) {
      if (notificationGroupMatches(group)) {
        logVerbose(`${closedCount}: FIND_CLOSE_ACTION`);
        let closeAction = findCloseAction(group, closedCount);

        if (notNull(closeAction)) {
          logVerbose(`${closedCount}: CLOSING`);
          try {
            closeAction.perform();
            logVerbose(`${closedCount}: CLOSE_PERFORMED`);
            return [true, 1];
          } catch (err) {
            logVerbose(`${closedCount}: Caught error while performing close action, window is probably closed.`);
            logVerbose(err);
          }
        }
        return [true, 0];
      }
    }
    return false;
  }

  const getNotificationCenter = () => {
    let systemEvents = Application("System Events");
    return systemEvents.processes.byName("NotificationCenter");
  }

  const getNotificationCenterGroups = () => {
    return getNotificationCenter().windows[0].uiElements[0].uiElements[0].uiElements();
  }

  let notificationCenter = getNotificationCenter();
  if (notificationCenter.windows.length <= 0) {
    return input;
  }

  let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length;

  if (groupsCount > 0) {
    logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);

    let closedCount = 0;
    let maybeMore = true;
    while (maybeMore) {
      let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount);
      maybeMore = closeResult[0];
      if (maybeMore) {
        closedCount = closedCount + closeResult[1];
      }
    }
  } else {
    throw Error(`No${appNameForLog} notifications found...`);
  }

  return input;
}
1
  • How do you run it? still in applescript editor?
    – majorgear
    Commented Nov 14, 2022 at 17:49
3

This has been annoying me too, but I managed to get something close working today.

First I installed cliclick, a command line tool that lets you simulate mouse movement and clicks. It's available via Homebrew with brew install cliclick.

Then I figured out the coordinates of the time in the top-right, which when clicked, opens the notification centre. This took a bit of experimentation.

Then I figured out the coordinates of the close button that appears on hover. Again this took a bit of experimentation.

Then I wrote a script that uses cliclick and these coordinates to open the Notification Center, move the mouse over where the "clear" button appears, sleeps for 500 milliseconds to give it time to appear, and then click it. It worked!

export PATH=/usr/local/bin:$PATH # make sure cliclick binary is on the path
cliclick c:1900,10               # click on the time to open Notification Center
cliclick -w 500 m:1570,45 c:.    # move mouse over the close button, wait for it to appear, then click it

I then tried to get this running as an Automator service. I got it running from within Automator but though the script worked there, it refused to work when I assigned it to a keyboard shortcut. I bashed my head against a brick wall trying out different permissions etc until giving up.

I tried the HotKey app, but had the same result.

Eventually I thought about trying an Alfred workflow, so I could open Alfred with Cmd+Space and then type "Clear notifications" to clear them. Much more verbose than a keyboard shortcut but... it worked! And no mouse needed. My best guess as to why is that when Alfred runs the script, it always runs from within Alfred, and Alfred already has the required permissions to move the mouse and click on stuff, but when I try to attach an Automator service to a keyboard shortcut, that service runs with whatever permissions the currently in focus app has.

This feels like a horribly brittle solution (what happens when I'm not using my external monitor??) and it seems rubbish that there's no built-in way to do this, but at least it works.

TL;DR a combination of a script using cliclick and an Alfred workflow to run it.

1
  • This is cool but is hard coded for your monitor dimensions. It's better if you move the mouse to the top right extreme and then use negative amounts to get to the required positions. Commented Dec 9, 2021 at 15:25
3

I've tried most of the solutions offered here, but none has quite worked as well as I want. I've since tried using Keysmith, and been pretty happy with the results.

Keysmith offers an existing shortcut for this. It looks like this:

Screenshot of a Keysmith shortcut to close notification

This gives you the ability to press ⌘ esc to close notifications (from any application).

The only issue I had with it was that once the notifications had been closed, the active window of the current application lost focus. Fortunately, you can add an additional step in the Keysmith shortcut to regain the focus of the active window: ⌃F4. That looks like this in Keysmith:

Screenshot of a Keysmith shortcut to close notifications. The shortcut is: in Notification center, "Click Close", and then "Press ⌃F4"

3
  • Cool app, thanks for the recommendation. Commented Dec 9, 2021 at 15:10
  • This doesn't work on Big Sur to close all if there is a group, it only works on a single notification. The Keysmith error says something like "cannot find element". Commented Dec 14, 2022 at 11:51
  • This also doesn't work on Monterrey to close all if there is a group, it only works on a single notification. Keysmith gives an error. Commented Jan 18, 2023 at 2:27
2

Thank you @lunzwell. I added a error routine.

on run
    try
        activate application "NotificationCenter"
        tell application "System Events"
            tell process "Mitteilungszentrale"
                set theWindow to group 1 of UI element 1 of scroll area 1 of window "Notification Center"
                # click theWindow
                set theActions to actions of theWindow
                repeat with theAction in theActions
                    if description of theAction is "Schließen" then
                        tell theWindow
                            perform theAction
                        end tell
                        exit repeat
                    end if
                end repeat
            end tell
        end tell
    on error e
        display dialog e
        activate
    end try
end run

I wonder though if there is a way to avoid an error message, when accidentally running the script with no notifications on the screen.

2
  • You also translated the script to German. :D
    – mattsson
    Commented Jan 6, 2021 at 14:07
  • Yep, sorry. I forgot about that. I needed to do that in order to work on my machine. I would be great if that would not be necessary.
    – Ptujec
    Commented Jan 7, 2021 at 19:25
2

I made some improvements to this script:

  1. I added error handling (a narrower trap, in English) /ht @Ptujec
  2. I recursively call this function after a delay (because I found that for some notifications, I would close, they would all disappear, but then all the others that were not the top in the stack would re-appear a few seconds later)
  3. I added support for the "Clear All" button (I found sometimes I had to use this instead of "Close")

My full script is in my dotfiles on GitHub here and I have it exposed via an Alfred Workflow here.

#!/usr/bin/osascript
# via:
# https://apple.stackexchange.com/questions/408019/dismiss-macos-big-sur-notifications-with-keyboard

# define a function we can call recursively
on dismiss_notification_center(n)
    log "dismiss_notification_center: " & n
    set performedAction to false
    activate application "NotificationCenter"
    tell application "System Events"
        tell process "Notification Center"
            try
                # when there are no notifications, this may result in:
                # 'System Events got an error: Can’t get window "Notification Center" of process "Notification Center".'
                # This is our recursion base case.
                set theWindow to group 1 of UI element 1 of scroll area 1 of window "Notification Center"
            on error e
                # log the error to the console:
                log quoted form of e
                return
            end try
            # log theWindow

            set theActions to actions of theWindow
            repeat with theAction in theActions
                # log theAction
                # log description of theAction
                if description of theAction is in {"Close", "Clear All"} then
                    tell theWindow
                        perform theAction
                        set performedAction to true
                    end tell
                    exit repeat
                end if
            end repeat
        end tell
    end tell
    # log "performedAction: " & performedAction
    if performedAction
        # for some reason, the loop doesn't close them all when grouped, so
        # we need to recurse. But, first we have to sleep to allow notif to re-appear:
        do shell script "/bin/sleep 3" # min sleep time that worked for me, and sometimes reminders take even longer
        dismiss_notification_center(n + 1)
    end if
end dismiss_notification_center

# first call to recursive function:
dismiss_notification_center(0)
2

I got a working script via cliclick also. Here's my version of the script I use. I've stitched together from a bunch of user libraries I used. I have it mapped to voice control created via Automator instead of a keyboard shortcut.

It still fails when the notification refuses to show the close button no matter how much you move the pointer on top of it.

activate application "NotificationCenter"
delay 0.1
tell application "System Events" to tell process "Notification Center"
    if (count of windows) is 0 then return 
    
    set theGroup to first group of UI element 1 of scroll area 1 of window "Notification Center"        
    tell theGroup
        if my isStacked(theGroup) then
            click theGroup
            delay 1
        end if
        
        my dismissNotification(theGroup)
    end tell
end tell

on isStacked(nextGroup)
    tell application "System Events" to tell process "Notification Center"          
        get help of nextGroup is "Activate to expand"
    end tell
end isStacked

to dismissNotification(theNotification)
    script CloseButtonClicker
        -- pointer's saveCurrentPosition()
        movePointer at theNotification
        delay 0.1
        tell application "System Events"
            try
                click button "Close" of theNotification
                return true
            end try
            delay 0.1
        end tell
        -- pointer's restorePosition()
    end script  
    exec on CloseButtonClicker for 3 by 0.4  -- Retry on failure up to 3x. Optional.
end dismissNotification

to movePointer at theUi
    set coord to getCoord at theUi
    set formattedCoord to formatCoordinates(item 1 of coord, item 2 of coord)

    set clickCommand to "/usr/local/bin/cliclick -e 1 m:" & formattedCoord
    do shell script clickCommand
end movePointer

to formatCoordinates(x, y)
    if x is less than 0 then set x to "=" & x
    if y is less than 0 then set y to "=" & y

    return x & "," & y as text
end formatCoordinates

to exec on scriptObj by sleep : 1 for ntimes : 1000
    repeat ntimes times
        try
            set handlerResult to run of scriptObj
            if handlerResult is not missing value then return         handlerResult
        end try 
        delay sleep
    end repeat
    return missing value
end exec
0

Here's what works for me on Big Sur. I found a script somewhere in stack exchange (possibly in this thread), but I ran into some errors and fixed them. I've been using it for a few weeks with no issues -- it's also much faster than the script I was using before Big Sur.

1. Create a clear_notifications script

You can do that by running this script from your terminal

write_target=~/bin/clear_notifications
mkdir ~/bin
cat << EOF > $write_target
#!/usr/bin/osascript

# Usage: clear_notifications [partial_title]
#
# Clears notifications from the notification center if they contain the string in [partial_title].
# If no arg is passed in, all notifications are cleared.

on run argv
  tell application "System Events"
     try
       set _groups to groups of UI element 1 of scroll area 1 of group 1 of window "Notification Center" of application process "NotificationCenter"
      repeat with _group in _groups
        set temp to value of static text 1 of _group
          set _actions to actions of _group # Get all the actions within this group
          set isInScope to true

          if (count of argv) > 0 then
            set searchTerm to item 1 of argv
            if temp does not contain searchTerm then
              log "Didn't find any notifications matching " & searchTerm
              set isInScope to false
            end if
          end if

          if isInScope then
            if exists (first action of _group where description is "Clear All") then
              log "Found 'clear all' for " & temp
              perform (first action of _group where description is "Clear All")
            else if exists (first action of _group where description is "Close") then
              log "Found close for " & temp
              perform (first action of _group where description is "Close")
            else
              log "Didn't find close action for " & temp
            end if
          end if

      end repeat
    on error errMsg
        log "Error: " & errMsg
    end try
  end tell
end run
EOF
chmod u+x $write_target

2. Connect the script to a keyboard shortcut.

You can do this using the keyboard shortcut instructions here, but I find automator clunky and cumbersome for such a simple task. I much prefer using Better Touch Tool, which I already use for many other shortcuts.

  • Go to Better Touch Tool configuration
  • In the left panel, select "All Apps"
  • Add a new keyboard shortcut of your choice and choose Execute Terminal Command (Async, non-blocking) as the trigger.
  • Enter ~/bin/clear_notifications into the textbox where it says Enter Terminal Command

better touch tool add shortcut

2. (Optional) Create shortcuts to clear specific notifications

If you just want to clear, say, Calendar notifications, where the title of the notification has the word "Calendar" in it, you can enter the following in the terminal command text box instead. I have terminal notifications from iTerm that let me know when builds are complete, and I mostly use it for that.

  • Enter ~/bin/clear_notifications "Calendar" into the textbox where it says Enter Terminal Command
-1

Had anyone considered just using:

pkill NotificationCenter

The process respawns after doing this. I use it to clear out all the notifications I get with an external drive.

1
  • 1
    The notifications come back almost immediately; You're probably on an old version of macOS.
    – HappyFace
    Commented Aug 17, 2021 at 9:05
-1

This answer suggests that on Big Sur in its current release there is no satisfactory way to dismiss notifications with a keyboard. Instead, this answer explains alternative ways to dismiss them that are not noted elsewhere in this thread.

Two-finger swipe right on the notification group works on Big Sur with the Macbook's trackpad (not sure about Yosemite). It also works with left-click and drag to the right. I assume this would work with an Apple Magic Mouse or Magic Trackpad as well. I personally find both the two-finger swipe and left-click and drag to be easier to do than clicking the X icon then clicking clear all. I find coordinating the twice-click in such a small area is very difficult.

This doesn't "clear" all notifications permanently. They will still show if you open the notification sidebar. They will also show if another message comes through; instead of seeing 1 notification it will show N + 1 notifications available.

This idea was noted in a comment in another answer: Dismiss MacOS Big Sur notifications with keyboard.

The reason I am suggesting that there is no satisfactory way to dismiss notifications with a keyboard is: an automated script is noticeably slow for large numbers of notifications, the Keysmith solution didn't work on Big Sur (it errored), pkill/killall NotificationCenter didn't work dismiss the notifications (which means a keyboard shortcut for it wouldn't help). I did not try the clickclick solution, I was concerned I would waste too much time trying to get it to work and that it would experience the same delays as an automated script.

This thread is similar to How do I clear All OS X notifications with 1 click?.

2
  • The question asks how to do with the keyboard. Your first paragraph is all bout mouse and trackpad.
    – mmmmmm
    Commented Dec 16, 2022 at 0:33
  • Yes, thank you for noting that. I modified it so that the disclaimer to the answer comes as the first paragraph instead of later in the answer. Commented Dec 17, 2022 at 4:37

Not the answer you're looking for? Browse other questions tagged .