---
title: "jQuery Timeline External Resource Drag & Drop Example | Mobiscroll"
component: "Timeline"
framework: "jQuery"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/jquery/timeline/resource-management-with-dynamic-team-creation"
---

# External Resource Drag & Drop - jQuery Timeline demo

Drag external resources onto a jQuery Timeline to add team members, or drag them off to remove them — build crew scheduling and team management.

- [Live demo](https://demo.mobiscroll.com/jquery/timeline/resource-management-with-dynamic-team-creation)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-jquery/tree/main/src/demos/eventcalendar/timeline/resource-management-with-dynamic-team-creation)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/timeline/resource-management-with-dynamic-team-creation.md), [Angular](https://demo.mobiscroll.com/angular/timeline/resource-management-with-dynamic-team-creation.md), [React](https://demo.mobiscroll.com/react/timeline/resource-management-with-dynamic-team-creation.md), [Vue](https://demo.mobiscroll.com/vue/timeline/resource-management-with-dynamic-team-creation.md)

## Description

By combining the `resourceReorder` feature with external resource drag-and-drop, you can easily organize, group, and adjust resources by dragging them into or out of the timeline view.

**Dragging resources onto the timeline**

Resources can be created by dragging and dropping an external element onto the timeline. In order for that to work you will need to have two things set up:

- Enable the timeline to receive external resources by setting [`externalResourceDrop`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-externalResourceDrop) to `true`.
- Initialize the external resources (containers) as `draggable` components.

Set up a container as draggable with the `mbsc-draggable` attribute. Pass a skeleton event definition through the `data-drag-data` attribute as a stringified json object. This will be added to the event calendar on drop. If omitted, a default event will be created. For a more programmatic approach you might want to use the component initialization where the `draggable` is initialized in js code and the `dragData` can be passed as an object.

Use the [`onResourceCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceCreate) for triggering a custom logic on drop like showing a toast.

**Dragging resources off the timeline)**

Additionally resource can be removed by dragging them out from the timeline and dropping them onto an external drop container. To activate this, you will need to:

- Enable the timeline to allow dragging events out by setting [`externalResourceDrag`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-externalResourceDrag) to `true`.
- Initialize the

[`dropcontainer`](https://mobiscroll.com/docs/jquery/eventcalendar/drag-and-drop#dropcontainer).

Resources can also be dropped on another timeline with `externalResourceDrop` enabled.

When a resource is dropped into an external drop container or another timeline, it will be deleted from the original calendar and the [`onResourceDelete`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceDelete) will be fired.

You can also use the [`onResourceDragLeave`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceDragLeave) and [`onResourceDragEnter`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceDragEnter), and the drop container's `onItemDragEnter` and `onItemDragLeave` events to provide visual feedback or running custom logic during drag.

**Looking for external event drag & drop?** [Check out this example →](https://demo.mobiscroll.com/jquery/timeline/external-drag-drop-sortable-dragula)

## What this demo shows

- A desktop day timeline for dynamic resource management, with the selected day arranged horizontally and a hierarchical installer team resource tree arranged vertically on the left.
- **External technician list** An external list beside the timeline shows available technicians that can be dragged into installer teams.
- **Header navigation** The month and year label opens date navigation, while the previous and next arrows and the Today button move between days or return to the current day.
- **Day strip** The fixed strip below the header shows the selected day using the `DD DDD MMM YYYY` date format, with the current date highlighted.
- **Time grid** The timeline shows working hours from 7 AM to 5 PM with hourly columns.
- **Resources** The resource column shows expandable and collapsible installer teams with technician child resources.
- **Resource rendering** Technician rows display avatar initials, the technician name, and the job title or specialization.
- **Adding teams** The resource header includes an Add team action for creating new empty installer teams.
- **Empty teams** Empty teams display a "Drag Technicians here" placeholder to indicate valid drop targets.
- **Team assignment** Technicians can be dragged from the external list into installer teams.
- **Team reorganization** Technicians can be dragged between teams to update team composition.
- **Drag-and-drop feedback** Drop targets are highlighted while a technician is being dragged.
- **Confirmation feedback** A toast notification confirms when a technician is assigned to a team.
- **Current time** A vertical blue line with a time label marks the current time.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Event cards** Events appear as colored cards with a left color stripe, job name, and exact start and end time.
- **Event positioning** Events are placed by assigned resource, date, start time, and end time.
- **Event interaction** Events highlight on hover and expose drag and resize handles for moving events or changing their duration.
- **Event selection** Clicking an event selects and highlights it.
- **Scrolling** Horizontal and vertical scrolling support larger technician lists, multiple installer teams, and extended schedules.

## Best for

- **Construction workforce planning** Organizing installers into teams and assigning scheduled work from the same timeline interface.
- **Field service dispatching** Managing technician availability, team assignments, and time-based jobs in a resource timeline.
- **Installer team management** Creating empty teams, filling them by drag and drop, and reorganizing team composition as plans change.
- **Crew scheduling** Coordinating multiple teams and technicians across a single workday with visible job timing.
- **Operational scheduling tools** Building scheduling interfaces where resource grouping, external resource drag and drop, and timeline-based job placement need to work together.

## 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
- [`context`](https://mobiscroll.com/docs/jquery/eventcalendar/drag-and-drop#opt-context) - option
- [`data`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-data) - option
- [`dragBetweenResources`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragBetweenResources) - option
- [`externalResourceDrop`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-externalResourceDrop) - option
- [`externalResourceDrag`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-externalResourceDrag) - option
- [`resources`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-resources) - option
- [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view) - option
- [`renderResourceHeader`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderResourceHeader) - renderer
- [`renderResource`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderResource) - renderer
- [`onResourceCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceCreate) - event
- [`onResourceDelete`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceDelete) - event
- [`onResourceOrderUpdate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onResourceOrderUpdate) - event
- [`setOptions`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setOptions) - method
- [`navigateToEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-navigateToEvent) - method

## Code

### JS

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

$(function () {
  var $availableInstallersList = $('#demo-ext-res-drop-list');

  var installers = [
    {
      id: 'it-1',
      name: 'Installer team 1',
      eventCreation: false,
      reorder: false,
      children: [
        {
          id: 1,
          name: 'Emily Carter',
          color: '#007acc',
          title: 'Electrician',
        },
        {
          id: 2,
          name: 'Michael Lawson',
          color: '#008000',
          title: 'Plumber',
        },
      ],
    },
    // Showing partial data. Download full source.
  ];

  var availableInstallers = [
    {
      id: 9,
      name: 'Adam Miller',
      color: '#C70039',
      title: 'Mason',
    },
    {
      id: 10,
      name: 'Isabella Martinez',
      color: '#2ECC71',
      title: 'Surveyor',
    },
    {
      id: 11,
      name: 'Mark White',
      color: '#34495E',
      title: 'Glazier',
    },
    // Showing partial data. Download full source.
  ];

  var tasks = [
    {
      id: 1,
      start: '2026-11-06T07:00',
      end: '2026-11-06T13:00',
      title: 'Electrical wiring installation',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-11-06T14:00',
      end: '2026-11-06T18:00',
      title: 'Power panel connection',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-11-06T08:00',
      end: '2026-11-06T12:00',
      title: 'Water pipe fitting',
      resource: 2,
    },
    // Showing partial data. Download full source.
  ];

  function generateExternalResourceItem(installer) {
    var item =
      '<div id="demo-ext-res-' +
      installer.id +
      '" class="mds-ext-res-item mbsc-font">' +
      '<div class="mbsc-flex">' +
      '<div class="mds-ext-res-avatar" style="background: ' +
      installer.color +
      '">' +
      installer.name[0] +
      '</div>' +
      '<div class="mds-ext-res-cont">' +
      '<div class="mds-ext-res-name">' +
      installer.name +
      '</div>' +
      '<div class="mds-ext-res-title">' +
      installer.title +
      '</div>' +
      '</div>' +
      '</div>';

    $availableInstallersList.append(item);

    return $('#demo-ext-res-' + installer.id)
      .mobiscroll()
      .draggable({
        context: '#demo-ext-res-page',
        dragData: installer,
        type: 'resource',
      });
  }

  var timelineInst = $('#demo-ext-res-drop-calendar')
    .mobiscroll()
    .eventcalendar({
      data: tasks,
      dragBetweenResources: false,
      externalResourceDrop: true,
      externalResourceDrag: true,
      resources: installers,
      view: {
        timeline: { type: 'day', resourceReorder: true, startTime: '07:00', endTime: '18:00', listing: true },
      },
      renderResourceHeader: function () {
        return (
          '<div class="mbsc-flex mbsc-align-items-center"><div class="mds-ext-res-header mbsc-flex-1-1">Set up teams</div>' +
          '<button mbsc-button id="demo-ext-res-add" data-variant="outline" class="mds-ext-res-add">Add team</button></div>'
        );
      },
      renderResource: function (resource) {
        return resource.isParent || resource.placeholder
          ? '<div class="mds-ext-res-name' + (resource.placeholder ? ' mds-ext-res-name-ph' : '') + '">' + resource.name + '</div>'
          : '<div class="mbsc-flex">' +
              '<div class="mds-ext-res-avatar" style="background: ' +
              resource.color +
              '">' +
              resource.name[0] +
              '</div>' +
              '<div class="mds-ext-res-cont">' +
              '<div class="mds-ext-res-name ' +
              '">' +
              resource.name +
              '</div>' +
              '<div class="mds-ext-res-title">' +
              resource.title +
              '</div>' +
              '</div>' +
              '</div>';
      },
      onResourceCreate: function (args) {
        var newResourceId = args.resource.id;
        availableInstallers = availableInstallers.filter(function (installer) {
          return installer.id !== newResourceId;
        });
        $('#demo-ext-res-' + newResourceId).remove();
        mobiscroll.toast({
          message: args.resource.name + ' added to ' + args.parent.name,
        });
      },
      onResourceDelete: function (args) {
        mobiscroll.toast({
          message: args.resource.name + ' removed from ' + args.parent.name,
        });
      },
      onResourceOrderUpdate: function (args) {
        installers = args.resources;
        var parent = args.parent;
        var oldParent = args.oldParent;

        if (parent && oldParent) {
          mobiscroll.toast({
            message: args.resource.name + ' moved to ' + args.parent.name,
          });
        }

        if (parent && parent.children) {
          // Remove placeholder resource
          parent.children = parent.children.filter(function (child) {
            return !child.placeholder;
          });
        }

        if (oldParent && !oldParent.children.length) {
          // Add placeholder resource
          oldParent.children.push({
            id: 'ph-' + oldParent.id,
            name: 'Drag Technicians here',
            placeholder: true,
            reorder: false,
          });
        }
      },
    })
    .mobiscroll('getInst');

  $('#demo-ext-res-drop-cont')
    .mobiscroll()
    .dropcontainer({
      onItemDrop: function (args) {
        var installer = args.data;
        if (installer && args.dataType === 'resource') {
          availableInstallers.push(installer);
          var item = generateExternalResourceItem(installer);
          item[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
        }
      },
    });

  $('#demo-ext-res-drop-calendar').on('click', '#demo-ext-res-add', function () {
    var teamLength = installers.length + 1;
    var resourceId = 'it-' + teamLength;
    installers.push({
      id: resourceId,
      eventCreation: false,
      reorder: false,
      name: 'Installer team ' + teamLength,
      children: [
        {
          id: 'ph-' + resourceId,
          name: 'Drag Technicians here',
          placeholder: true,
          reorder: false,
        },
      ],
    });

    timelineInst.setOptions({ resources: installers.slice() });

    setTimeout(function () {
      timelineInst.navigateToEvent({
        start: new Date(),
        resource: resourceId,
      });
    });
  });

  availableInstallers.forEach(generateExternalResourceItem);
});
```

### HTML

```html
<div id="demo-ext-res-page" class="mds-ext-res-drop" mbsc-page>
  <div class="mbsc-grid mbsc-no-padding">
    <div class="mbsc-row">
      <div id="demo-ext-res-drop-cont" class="mbsc-col-sm-3 mbsc-flex-col mds-ext-res-drop-cont">
        <div class="mds-ext-res-header">Available technicians</div>
        <div id="demo-ext-res-drop-list" class="mds-ext-res-list"></div>
      </div>
      <div class="mbsc-col-sm-9 mds-ext-res-drop-calendar">
        <div id="demo-ext-res-drop-calendar"></div>
      </div>
    </div>
  </div>
</div>
```

### CSS

```css
.mds-ext-res-drop-calendar {
  border-left: 1px solid #ccc;
}

.mds-ext-res-drop-calendar .mbsc-timeline-row { 
  height: 50px; 
}

.mds-ext-res-drop-calendar .mbsc-timeline-row-gutter { 
  height: 8px; 
}

.mds-ext-res-drop-calendar .mbsc-timeline-resource-col {
  width: 250px;
}

.mds-ext-res-drop-calendar .mbsc-timeline-parent { 
  height: 32px; 
}

.mds-ext-res-drop-calendar .mbsc-timeline-resource {
  align-items: center;
  align-content: center;
}

.mds-ext-res-drop-calendar .mds-ext-res-add {
  margin: 0;
}

.mds-ext-res-header {
  font-weight: 600;
  font-size: .875em;
  line-height: 38px;
  text-transform: uppercase;
  opacity: 0.6;
}

.mds-ext-res-name-ph {
  font-style: italic;
  opacity: 0.4;
}

.mds-ext-res-drop-cont .mds-ext-res-header {
  padding: 12px 16px;
  line-height: 1.5em;
}

.mds-ext-res-list {
  overflow: auto;
}

.mds-ext-res-item {
  background: rgba(128, 128, 128, .6);
  padding: 10px;
  margin: 16px;
  border-radius: 8px;
}

.mds-ext-res-item:first-child {
  margin-top: 0
}

.mds-ext-res-cont {
  padding: 0 7px;
}

.mds-ext-res-name {
  font-size: 14px;
  font-weight: 600;
}

.mds-ext-res-title {
  padding-top: 2px;
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
}

.mds-ext-res-avatar {
  width: 36px;
  min-width: 36px;
  height: 36px;
  line-height: 36px;
  border-radius: 18px;
  text-align: center;
  color: #fff;
  font-size: 22px;
  font-weight: 400;
}

.mds-ext-res-drop,
.mds-ext-res-drop .mbsc-grid,
.mds-ext-res-drop .mbsc-row,
.mds-ext-res-drop-cont,
.mds-ext-res-drop-calendar {
  height: 100%;
}
```

## Implementation notes

- Use `type: 'day'` with `startTime: '07:00'`/`endTime: '18:00'` and `resourceReorder: true` in the view config to allow drag-reordering of resources within the timeline.
- Set `externalResourceDrop: true` to allow resources dragged from the sidebar to be added to the timeline, and `externalResourceDrag: true` to allow timeline resources to be dragged back out. Set `dragBetweenResources: false` to prevent events from being reassigned across team members.
- Define the timeline resources as a two-level tree of team groups (`eventCreation: false`, `reorder: false`) with installer leaf nodes (custom `name`, `color`, `title`). When a team has no members, give it a single placeholder child (`placeholder: true`, `reorder: false`) with a descriptive name like "Drag Technicians here".
- Build a separate `availableInstallers` state for the sidebar list. Render each available installer as an `Installer` card component that uses a ref callback to obtain its DOM element, then mounts a `Draggable` with `dragData` set to the resource object and `type: 'resource'`.
- Wrap the sidebar list in a `Dropcontainer` (with its DOM element passed via a ref callback to the `element` prop). In `onItemDrop`, check that `args.dataType === 'resource'` and add `args.data` back to `availableInstallers`. After adding, call `scrollIntoView` on the last card's ref to bring the returned installer into view.
- In `onResourceCreate`, remove the dropped installer from `availableInstallers` by ID, and show a `Toast` announcing that the person was added to `args.parent.name`.
- In `onResourceDelete`, show a `Toast` announcing the removal from `args.parent.name`.
- In `onResourceOrderUpdate`, show a `Toast` when `args.parent` and `args.oldParent` both exist (indicating a cross-team move). Remove the placeholder child from the destination team and, if the source team is now empty, push a new placeholder child onto its `children` array.
- Use `renderResourceHeader` (Angular: `resourceHeaderTemplate`, Vue: `#resourceHeader` slot) to render a "Set up teams" label alongside an "Add team" `Button`.
- Use `renderResource` (Angular: `resourceTemplate`, Vue: `#resource` slot) to handle three cases: parent/group nodes render just the team name; placeholder nodes render a styled placeholder label; leaf nodes render a colored avatar (first letter of the name), the installer's name, and their job title.
- In the Add team handler, append a new group to the installers array with a placeholder child, then call `navigateToEvent` on the calendar instance ref with the new resource ID to scroll the timeline to the newly added team.

## 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: [Resource Search and Filtering](https://demo.mobiscroll.com/jquery/timeline/resource-filtering-search.md) - Add live search and filter to a jQuery Timeline resource header — multi-select by status or group, with a custom empty state and reset.
- Next: [Event Grouping & Bulk Actions](https://demo.mobiscroll.com/jquery/timeline/event-grouping-bulk-action.md) - Group jQuery Timeline events into collapsible client/quarter blocks and bulk-drag groups — switch between assignee and work-type, on a year view.
