---
title: "jQuery Event calendar Bulk Actions Example | Mobiscroll"
component: "Event calendar"
framework: "jQuery"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/jquery/eventcalendar/event-bulk-actions-edit-delete-update"
---

# Bulk Actions - jQuery Event calendar demo

Multi-select events by keyboard in a jQuery calendar - bulk delete, update, or run custom actions via action panel or right-click context menu.

- [Live demo](https://demo.mobiscroll.com/jquery/eventcalendar/event-bulk-actions-edit-delete-update)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-jquery/tree/main/src/demos/eventcalendar/calendar-view/event-bulk-actions-edit-delete-update)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/eventcalendar/event-bulk-actions-edit-delete-update.md), [Angular](https://demo.mobiscroll.com/angular/eventcalendar/event-bulk-actions-edit-delete-update.md), [React](https://demo.mobiscroll.com/react/eventcalendar/event-bulk-actions-edit-delete-update.md), [Vue](https://demo.mobiscroll.com/vue/eventcalendar/event-bulk-actions-edit-delete-update.md)

## Description

Multiple event selection can be turned on with the [`selectMultipleEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-selectMultipleEvents) option. Selecting multiple events can be done through user interaction with `CTRL`/`SHIFT`/`CMD` + click or programmatically eg. click of a button or 'select all' checkbox.

Bulk operations like delete, update can be applied the selected events. Things like deleting with the `backspace` or `delete` buttons work out of the box but custom actions can be also applied. The selection can be easily retrieved and updated with the [`getSelectedEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-getSelectedEvents) and [`setSelectedEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setSelectedEvents) method.

Custom actions can be performed with external buttons or with context menu activated on right-click.

**Interested in moving events between two separate calendars?** [Check out the next example for cut, copy, paste events →](https://demo.mobiscroll.com/jquery/eventcalendar/cut-copy-paste-events-between-calendars)

## What this demo shows

- A desktop month view event calendar that supports selecting and managing multiple events from the current view.
- **Calendar** A full month calendar displays multiple events, with all events visible and row height expanding to fit the content.
- **Calendar header** The header shows the current month and year on the left, and blue month navigation arrows with a `Today` button (for jumping back to the current date) between them on the right. 
- **Bulk action panel** A side panel next to the calendar contains `Select all from view`, `Reset selection`, and `Update selected` actions.
- **Select all from view** Clicking this action selects all events currently visible and highlights thei titles in the month view and shows a bottom-centered toast message: `All events selected from the view`.
- **Reset selection** Clicking this action clears the current selection and shows a bottom-centered toast message: `Selection cleared`.
- **Update selected** Clicking this action changes the color of all selected events to orange and shows a bottom-centered toast message: `All selected event's color changed to orange`.
- **Currently selected** The side panel includes a `Currently selected` section that is empty by default and lists the titles of all selected events when one or more events are selected.
- **Multi-selection** Users can select multiple events with `CTRL`/`SHIFT`/`CMD` + click.
- **Context menu** Right-clicking an event opens a small popup with `Update` and `Delete` actions for that event.
- **Label interaction** Hovering over or clicking an event label selects it and highlights the selected label.
- **Day cell states for future days** Hovering a day cell highlights the day number with a gray background, while clicking the empty part of the cell selects the day and highlights the day number with a blue background.

## Best for

- **Scheduling tools** Calendar-based scheduling interfaces where users need to review and manage many visible events in a month view.
- **Resource management** Resource planning workflows that require selecting and adjusting multiple bookings, assignments, or availability blocks together.
- **Operations teams** Internal tools for managing appointments, team shifts, project deadlines, or other time-based records in batches.
- **Mixed individual and batch actions** Use cases that need both multi-event actions and single-event actions from a contextual right-click menu.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-themeVariant) - option
- [`getSelectedEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-getSelectedEvents) - method
- [`updateEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-updateEvent) - method
- [`removeEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-removeEvent) - method
- [`addEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-addEvent) - method
- [`setSelectedEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setSelectedEvents) - method
- [`clickToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-eventDelete) - option
- [`selectMultipleEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-selectMultipleEvents) - option
- [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view) - option
- [`labels`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-labels) - option
- [`onEventUpdate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventUpdate) - event
- [`onEventDelete`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventDelete) - event
- [`onPageLoading`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onPageLoading) - event
- [`onEventRightClick`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventRightClick) - event
- [`setOptions`](https://mobiscroll.com/docs/jquery/select/api#method-setOptions) - method
- [`anchor`](https://mobiscroll.com/docs/jquery/select/api#opt-anchor) - option
- [`open`](https://mobiscroll.com/docs/jquery/select/api#method-open) - method
- [`onSelectedEventsChange`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onSelectedEventsChange) - event
- [`touchUi`](https://mobiscroll.com/docs/jquery/select/api#opt-touchUi) - option
- [`display`](https://mobiscroll.com/docs/jquery/select/api#opt-display) - option
- [`buttons`](https://mobiscroll.com/docs/jquery/select/api#opt-buttons) - option
- [`inputElement`](https://mobiscroll.com/docs/jquery/select/api#opt-inputElement) - option
- [`onChange`](https://mobiscroll.com/docs/jquery/select/api#event-onChange) - event
- [`onClose`](https://mobiscroll.com/docs/jquery/select/api#event-onClose) - event
- [`setVal`](https://mobiscroll.com/docs/jquery/select/api#method-setVal) - method
- [`getEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-getEvents) - method
- [`setEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setEvents) - method

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

$(function () {
  var confirmOpen;
  var firstDay;
  var lastDay;
  var selectedEvent;
  var formatDate = mobiscroll.formatDate;

  function getSelectedEventTitles(events) {
    var eventTitles = [];
    for (var i = 0; i < events.length; ++i) {
      eventTitles.push(events[i].title);
    }
    return eventTitles;
  }

  function deleteSelectedEvents() {
    var selectedEvents = calendar.getSelectedEvents();
    var events = selectedEvents.length == 0 && selectedEvent ? [selectedEvent] : selectedEvents;

    confirmOpen = true;

    mobiscroll.confirm({
      title: 'Are you sure you want to delete the following events?',
      message: getSelectedEventTitles(events).join(', '),
      okText: 'Delete',
      callback: function (result) {
        if (result) {
          var eventsToDelete = [];
          var eventsToUpdate = [];

          for (var i = 0; i < events.length; ++i) {
            var event = events[i];
            if (event.recurring) {
              var origEvent = event.original;
              var exc = origEvent.recurringException || [];
              exc.push(event.start);
              origEvent.recurringException = exc;
              eventsToUpdate.push(origEvent);
            } else {
              eventsToDelete.push(event);
            }
          }
          calendar.updateEvent(eventsToUpdate);
          calendar.removeEvent(eventsToDelete);
          selectEvents([]);
          mobiscroll.toast({
            message: 'Deleted',
          });
        }
        confirmOpen = false;
      },
    });
  }

  function updateSelectedEvents() {
    var selectedEvents = calendar.getSelectedEvents();
    var events = selectedEvents.length == 0 && selectedEvent ? [selectedEvent] : selectedEvents;
    var eventsToUpdate = [];
    var newEvents = [];

    for (var i = 0; i < events.length; ++i) {
      var event = events[i];
      if (event.recurring) {
        var origEvent = event.original;
        var exc = origEvent.recurringException || [];
        var newEvent = event;

        newEvent.recurring = undefined;
        newEvent.color = 'orange';
        newEvent.id += '_' + formatDate('YYYY-MM-DD', event.start);
        newEvents.push(newEvent);

        exc.push(event.start);
        origEvent.recurringException = exc;
        eventsToUpdate.push(origEvent);
      } else {
        event.color = 'orange';
        eventsToUpdate.push(event);
      }
    }
    calendar.updateEvent(eventsToUpdate);
    calendar.addEvent(newEvents);
    selectEvents([]);

    mobiscroll.toast({
      message: "All selected event's color changed to orange",
    });
  }

  function selectEvents(events) {
    var titles = getSelectedEventTitles(events);
    var list = $('<ul/>');
    var item;

    for (var i = 0; i < titles.length; ++i) {
      item = $('<li/>').html(titles[i]);
      list.append(item);
    }

    $('#selected-event-list').html(list);
    calendar.setSelectedEvents(events);
  }

  var calendar = $('#demo-multiple-event-selection')
    .mobiscroll()
    .eventcalendar({
      clickToCreate: true,
      dragToCreate: true,
      dragToMove: true,
      dragToResize: true,
      eventDelete: true,
      selectMultipleEvents: true,
      view: {
        calendar: {
          labels: 'all',
        },
      },
      onEventUpdate: function (args) {
        if (args.isDelete) {
          if (!confirmOpen) {
            deleteSelectedEvents();
          }
          return false;
        }
      },
      onEventDelete: function () {
        if (!confirmOpen) {
          deleteSelectedEvents();
          return false;
        }
      },
      onPageLoading: function (args) {
        firstDay = args.firstDay;
        lastDay = args.lastDay;
      },
      onEventRightClick: function (args) {
        selectedEvent = args.event;
        args.domEvent.preventDefault();
        menu.setOptions({
          anchor: args.domEvent.target,
        });
        setTimeout(function () {
          menu.open();
        });
      },
      onSelectedEventsChange: function (args) {
        selectEvents(args.events);
      },
    })
    .mobiscroll('getInst');

  var menu = $('#demo-context-menu')
    .mobiscroll()
    .select({
      touchUi: false,
      display: 'anchored',
      buttons: [],
      inputElement: document.getElementById('context-menu-input'),
      onChange: function (args) {
        if (args.value === 'update') {
          updateSelectedEvents();
        } else if (args.value === 'delete') {
          deleteSelectedEvents();
        }
      },
      onClose: function () {
        // Clear selection
        menu.setVal();
      },
    })
    .mobiscroll('getInst');

  $('#select-all-events').on('click', function () {
    selectEvents(calendar.getEvents(firstDay, lastDay));
    mobiscroll.toast({
      message: 'All events selected from view',
    });
  });

  $('#reset-selection').on('click', function () {
    selectEvents([]);
    mobiscroll.toast({
      message: 'Selection cleared',
    });
  });

  $('#update-selected').on('click', function () {
    updateSelectedEvents();
  });

  $('.md-bulk-operations').keydown(function (ev) {
    if (!confirmOpen && (ev.keyCode === 8 || ev.keyCode === 46)) {
      deleteSelectedEvents();
    }
  });

  $.getJSON(
    'https://trial.mobiscroll.com/events/?vers=5&callback=?',
    function (events) {
      calendar.setEvents(events);
    },
    'jsonp',
  );
});
```

### HTML

```html
<div mbsc-page class="md-bulk-operations">
    <div class="mbsc-grid mbsc-no-padding">
        <div class="mbsc-row">
            <div class="mbsc-col-sm-9 mbsc-push-sm-3">
                <div id="demo-multiple-event-selection" class="md-bulk-operations-border"></div>
                <input id="context-menu-input" type="hidden" />
                <select id="demo-context-menu">
                    <option value="update">Update</option>
                    <option value="delete">Delete</option>
                </select>
            </div>
            <div class="mbsc-col-sm-3 mbsc-pull-sm-9">
                <div class="mbsc-form-group">
                    <div class="mbsc-button-group-block">
                        <button id="select-all-events" mbsc-button>Select all from view</button>
                        <button id="reset-selection" mbsc-button>Reset selection</button>
                        <button id="update-selected" mbsc-button>Update selected</button>
                    </div>
                </div>
                <div class="mbsc-form-group-title">Currently selected</div>
                <div id="selected-event-list" class="mbsc-padding md-selected-event-list"></div>
            </div>
        </div>
    </div>
</div>
```

### CSS

```css
.md-bulk-operations-border {
    border-left: 1px solid #ccc;
}

.md-selected-event-list {
    height: 500px;
    overflow: auto;
    padding-top: 0;
    padding-bottom: 0;
}
```

## Implementation notes

- Use `view: { calendar: { labels: true } }`. Set `selectMultipleEvents: true` to enable Ctrl/Shift/Cmd+click multi-selection. Load events from `https://trial.mobiscroll.com/events/?vers=5` via JSONP. React/Angular/Vue: assign the fetched array to the state/property bound to the `data` prop. JS/jQuery: call `inst.setEvents(events)` on load.
- Use a two-column layout: the calendar occupies most of the space; the sidebar contains three action buttons and a scrollable list of selected event titles. Button labels: React/Vue use "Select all this month"; Angular/JS/jQuery use "Select all from view"; all frameworks use "Reset selection" and "Update selected".
- Track `firstDay` and `lastDay` via `onPageLoading` (`args.firstDay`, `args.lastDay`). `onSelectedEventsChange` → update the current selection and refresh the displayed titles list.
- **"Select all"**: call `inst.getEvents(firstDay, lastDay)` to retrieve visible events, then set them as selected; for the imperative API, call `inst.setSelectedEvents(events)`. Show toast.
- **"Reset selection"**: clear `selectedEvents`; for the imperative API, call `inst.setSelectedEvents([])`. Show toast.
- **"Update selected"**: iterate the selected events. For **recurring occurrences**: clear `recurring`, set `color = 'orange'`, append `'_' + formatDate('YYYY-MM-DD', event.start)` to the `id`, push `event.start` to `origEvent.recurringException`, then persist the detached event and the updated original. For **non-recurring**: set `color = 'orange'` and update in place. Update the events array in place; for the imperative API, call `inst.updateEvent(eventsToUpdate)` and `inst.addEvent(newEvents)`. Reset selection, show toast.
- **`deleteSelectedEvents()`**: show a confirm dialog ("Are you sure you want to delete the following events?" / okText: "Delete") with selected event titles. On confirm: for **recurring** events push `event.start` to `origEvent.recurringException` and update the original; for **non-recurring**, remove from the events array; for the imperative API, call `inst.removeEvent(eventsToDelete)` and `inst.setSelectedEvents([])`. Reset selection, show "Deleted" toast.
- **Delete entry points**: (1) **`onEventDelete`** - `return false` (all frameworks); (2) **`onEventUpdate` with `args.isDelete === true`** - `return false` (React/Angular/JS/jQuery; Vue omits `onEventUpdate`); (3) **context menu "Delete"**. Use a `confirmOpen` flag to prevent double-triggering from (1) and (2).
- **Right-click context menu**: `onEventRightClick` → `args.domEvent.preventDefault()`, open an anchored `Select` popup with "Update" / "Delete" options. Imperative API: also store `selectedEvent = args.event` as a fallback - used in `deleteSelectedEvents` and `updateSelectedEvents` when `inst.getSelectedEvents()` returns an empty array.
- **Keyboard delete**: attach a `keydown` listener on the container; if Delete or Backspace is pressed and no confirm dialog is open, call `deleteSelectedEvents()`.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-jquery.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config jquery
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config jquery --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/jquery/getting-started/installation

## Related demos

- Previous: [Block Past Event Creation](https://demo.mobiscroll.com/jquery/eventcalendar/disallow-past-event-creation.md) - Keep past dates read-only in a jQuery event calendar — block creation and editing on past days and show toast feedback on failed attempts.
- Next: [Cut, Copy, Paste Events](https://demo.mobiscroll.com/jquery/eventcalendar/cut-copy-paste-events-between-calendars.md) - Transfer events between two jQuery event calendars with cut, copy, paste, and undo — using keyboard shortcuts or a right-click menu.
