---
title: "JQuery Timeline Task Progress Tracking Example | Mobiscroll"
component: "Timeline"
framework: "jQuery"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/jquery/timeline/show-task-progress-on-event"
---

# Task Progress Tracking - jQuery Timeline demo

Show task completion progress on JQuery Timeline events with an inline hover slider for live updates and a progress field in the add/edit dialog.

- [Live demo](https://demo.mobiscroll.com/jquery/timeline/show-task-progress-on-event)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-jquery/tree/main/src/demos/eventcalendar/timeline/show-task-progress-on-event)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/timeline/show-task-progress-on-event.md), [Angular](https://demo.mobiscroll.com/angular/timeline/show-task-progress-on-event.md), [React](https://demo.mobiscroll.com/react/timeline/show-task-progress-on-event.md), [Vue](https://demo.mobiscroll.com/vue/timeline/show-task-progress-on-event.md)

## Description

Showing percentage progress, or even having a visual overlay that indicates the progress of a task is sometimes necessary, not just for full fledged Gantt charts. Although being usually associated with Gantt charts, having a clear overview of progress with tools that enable live update is useful in a lot of situations.

Using the event templating capabilities, it is possible to render a live slider that reacts on hover and enables updating the progress inline. Use the [`renderTimelineEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/timeline#renderer-renderTimelineEvent) function to customize the template and adjust it to your needs.

Besides being able to update progress right on the event, it is also possible to add a slider that controls task progression - saved in a custom property of the event - inside an add/edit dialog.

## What this demo shows

- A desktop monthly timeline for project planning and progress tracking, with days arranged horizontally and resources listed vertically on the left.
- **Header navigation** The month and year label opens date navigation. The previous and next arrows and the Today button move between months and return to the current day.
- **Timeline header** The fixed date strip shows the days of the selected month in `D DDD` format, with the current date highlighted.
- **Resources** Hierarchical resource groups organize team members into departments such as Designer Team, Development Team, and QA Team.
- **Resource details** Individual resources show custom information, including the team member's role or job title.
- **Event colors** Events for the same team member or group keep a consistent color in the timeline.
- **Event cards** Events are rendered as colored cards with the project name, a progress percentage, and an inline progress slider.
- **Inline progress** The progress slider reacts on hover and lets users update completion directly on the event.
- **Event positioning** Events are placed by assigned resource and by start and end date.
- **Event creation** Users can create events by double-clicking a resource row or by clicking and dragging across a date range.
- **Add form** Creating an event opens a form with title, start date, end date, and progress slider fields, plus Cancel and Add actions.
- **Event editing** Clicking an event selects it and opens a popup for modifying the event details.
- **Drag and resize** Events show drag and resize handles on hover for moving tasks or changing their duration.
- **Vertical scrolling** The resource area scrolls vertically so the timeline can display more resources in the same view.

## Best for

- **Project management** Tracking tasks, ownership, timing, and completion progress in a single timeline.
- **Sprint planning** Planning work across team members and date ranges while keeping progress visible.
- **Software development teams** Coordinating designers, developers, and QA resources across overlapping project tasks.
- **Task tracking** Updating task progress directly from the timeline without opening a separate detail view.
- **Team workload management** Reviewing who is assigned to what, when each task runs, and how far along the work is.
- **Gantt-style planning** Showing project spans and completion percentages without requiring a full Gantt chart implementation.

## 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
- [`setOptions`](https://mobiscroll.com/docs/jquery/popup/api#method-setOptions) - method
- [`anchor`](https://mobiscroll.com/docs/jquery/popup/api#opt-anchor) - option
- [`headerText`](https://mobiscroll.com/docs/jquery/popup/api#opt-headerText) - option
- [`buttons`](https://mobiscroll.com/docs/jquery/popup/api#opt-buttons) - option
- [`updateEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-updateEvent) - method
- [`close`](https://mobiscroll.com/docs/jquery/popup/api#method-close) - method
- [`cssClass`](https://mobiscroll.com/docs/jquery/popup/api#opt-cssClass) - option
- [`onClose`](https://mobiscroll.com/docs/jquery/popup/api#event-onClose) - event
- [`removeEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-removeEvent) - method
- [`open`](https://mobiscroll.com/docs/jquery/popup/api#method-open) - method
- [`dragToMove`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToResize) - option
- [`dragToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToCreate) - option
- [`clickToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-clickToCreate) - option
- [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-data) - option
- [`resources`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-resources) - option
- [`onEventClick`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventClick) - event
- [`onEventCreated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventCreated) - event
- [`onEventUpdated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventUpdated) - event
- [`renderTimelineEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderTimelineEvent) - renderer
- [`renderResource`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderResource) - renderer
- [`select`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-select) - option
- [`startInput`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-startInput) - option
- [`endInput`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-endInput) - option
- [`showRangeLabels`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-showRangeLabels) - option
- [`touchUi`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-touchUi) - option
- [`responsive`](https://mobiscroll.com/docs/jquery/datepicker/api#opt-responsive) - option
- [`onChange`](https://mobiscroll.com/docs/jquery/datepicker/api#event-onChange) - event
- [`display`](https://mobiscroll.com/docs/jquery/popup/api#opt-display) - option
- [`fullScreen`](https://mobiscroll.com/docs/jquery/popup/api#opt-fullScreen) - option
- [`scrollLock`](https://mobiscroll.com/docs/jquery/popup/api#opt-scrollLock) - option
- [`responsive`](https://mobiscroll.com/docs/jquery/popup/api#opt-responsive) - option
- [`width`](https://mobiscroll.com/docs/jquery/popup/api#opt-width) - option
- [`touchUi`](https://mobiscroll.com/docs/jquery/popup/api#opt-touchUi) - option

## Code

### JS

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

$(function () {
  var eventId;
  var eventTitle;
  var eventStart;
  var eventEnd;
  var eventProgress;
  var isDraggingProgress;

  var $eventProgressLabel = $('.mds-popup-progress-label');
  var $eventProgress = $('.mds-popup-progress-slider');
  var $eventTitle = $('.mds-popup-event-title');

  var myEvents = [
    {
      start: '2026-10-22T00:00',
      end: '2026-10-25T00:00',
      title: 'Design Homepage',
      resource: 'alice',
      progress: 100,
    },
    {
      start: '2026-10-22T00:00',
      end: '2026-10-26T00:00',
      title: 'Create Wireframes',
      resource: 'bob',
      progress: 100,
    },
    {
      start: '2026-10-24T00:00',
      end: '2026-10-29T00:00',
      title: 'Develop Frontend',
      resource: 'charlie',
      progress: 45,
    },
    {
      start: '2026-10-24T00:00',
      end: '2026-10-29T00:00',
      title: 'Develop Backend',
      resource: 'dave',
      progress: 35,
    },
    {
      start: '2026-10-29T00:00',
      end: '2026-11-02T00:00',
      title: 'Test Website',
      resource: 'erin',
      progress: 0,
    },
    {
      start: '2026-10-26T00:00',
      end: '2026-11-02T00:00',
      title: 'Fix Bugs',
      resource: 'frank',
      progress: 0,
    },
    {
      start: '2026-11-02T00:00',
      end: '2026-11-05T00:00',
      title: 'Deploy Website',
      resource: 'george',
      progress: 0,
    },
  ];

  var myResources = [
    {
      id: 'gro1',
      name: 'Designer Team',
      color: '#76e083',
      eventCreation: false,
      children: [
        {
          id: 'alice',
          name: 'Alice',
          title: 'Designer',
          color: '#1dab2f',
        },
        {
          id: 'bob',
          name: 'Bob',
          title: 'Designer',
          color: '#76e083',
        },
      ],
    },
    {
      id: 'gro2',
      name: 'Development Team',
      color: '#ff1717',
      eventCreation: false,
      children: [
        {
          id: 'charlie',
          name: 'Charlie',
          title: 'Frontend Developer',
          color: '#4981d6',
        },
        {
          id: 'dave',
          name: 'Dave',
          title: 'Backend Developer',
          color: '#f7961e',
        },
        {
          id: 'frank',
          name: 'Frank',
          title: 'Full-Stack Developer',
          color: '#34c8e0',
        },
        {
          id: 'george',
          name: 'George',
          title: 'DevOps Engineer',
          color: '#e25dd2',
        },
      ],
    },

    {
      id: 'gro3',
      name: 'QA Team',
      color: '#d6d145',
      eventCreation: false,
      children: [
        {
          id: 'erin',
          name: 'Erin',
          title: 'QA Tester',
          color: '#d6d145',
        },
      ],
    },
  ];

  function renderEvent(data) {
    var event = data.original;
    var color = data.color;
    var progress = event.progress || 0;

    return (
      '<div class="mds-progress-event" style="background:' +
      color +
      ';">' +
      '<div class="mds-progress-bar" style="width:' +
      progress +
      '%;">' +
      '<div class="mds-progress-arrow" data-event-id="' +
      event.id +
      '"></div>' +
      '</div>' +
      '<div class="mds-progress-event-content">' +
      '<div class="mds-progress-event-title">' +
      event.title +
      '</div>' +
      '</div>' +
      '<div class="mds-progress-label" >' +
      progress +
      '%</div>' +
      '</div>'
    );
  }

  function renderCustomResource(resource) {
    return (
      '<div class="mds-progress-employee-name">' +
      resource.name +
      '</div>' +
      (resource.title !== undefined ? '<div class="mds-progress-employee-title">' + resource.title + '</div>' : '') +
      '</div>'
    );
  }

  function createAddPopup(event, target) {
    var success = false;

    addEditPopup.setOptions({
      anchor: target,
      headerText: 'New event',
      buttons: [
        'cancel',
        {
          text: 'Add',
          keyCode: 'enter',
          handler: function () {
            event.title = eventTitle;
            event.start = eventStart;
            event.end = eventEnd;
            event.progress = eventProgress;
            myEvents.push(event);
            calendar.updateEvent(event);
            success = true;
            addEditPopup.close();
          },
          cssClass: 'mbsc-popup-button-primary',
        },
      ],
      onClose: function () {
        if (!success) {
          calendar.removeEvent(event);
        }
      },
    });

    fillPopup(event);
    addEditPopup.open();
  }

  function createEditPopup(event, target) {
    addEditPopup.setOptions({
      headerText: 'Edit event',
      anchor: target,
      buttons: [
        'cancel',
        {
          text: 'Save',
          keyCode: 'enter',
          handler: function () {
            var eventToUpdate = myEvents.find(function (event) {
              return event.id === eventId;
            });
            eventToUpdate.title = eventTitle;
            eventToUpdate.start = eventStart;
            eventToUpdate.end = eventEnd;
            eventToUpdate.progress = eventProgress;
            calendar.updateEvent(eventToUpdate);
            addEditPopup.close();
          },
          cssClass: 'mbsc-popup-button-primary',
        },
      ],
    });

    fillPopup(event);
    addEditPopup.open();
  }

  function fillPopup(event) {
    eventId = event.id;
    eventTitle = event.title || '';
    eventStart = event.start;
    eventEnd = event.end;
    eventProgress = event.progress || 0;

    $eventTitle.mobiscroll('getInst').value = eventTitle;
    $eventProgressLabel.text(eventProgress + ' %');
    $eventProgress.val(eventProgress);
    eventStartEndPicker.setVal([eventStart, eventEnd]);
  }

  var calendar = $('#demo-task-progression')
    .mobiscroll()
    .eventcalendar({
      class: 'mds-progress-calendar',
      dragToMove: true,
      dragToResize: true,
      dragToCreate: true,
      clickToCreate: true,
      view: { timeline: { type: 'month', eventDisplay: 'fill' } },
      data: myEvents,
      resources: myResources,
      onEventClick: function (args) {
        if (isDraggingProgress) {
          isDraggingProgress = false;
          return;
        }
        createEditPopup(args.event, args.domEvent.currentTarget);
      },
      onEventCreated: function (args) {
        createAddPopup(args.event, args.target);
      },
      onEventUpdated: function (args) {
        var eventToUpdate = myEvents.find(function (event) {
          return event.id === args.event.id;
        });
        eventToUpdate.start = args.event.start;
        eventToUpdate.end = args.event.end;
        eventToUpdate.resource = args.event.resource;
      },
      renderTimelineEvent: renderEvent,
      renderResource: renderCustomResource,
    })
    .mobiscroll('getInst');

  var eventStartEndPicker = $('#popup-event-dates')
    .mobiscroll()
    .datepicker({
      select: 'range',
      startInput: '#popup-event-start',
      endInput: '#popup-event-end',
      showRangeLabels: false,
      touchUi: true,
      responsive: { medium: { touchUi: false } },
      onChange: function (args) {
        var dates = args.value;
        eventStart = dates[0];
        eventEnd = dates[1];
      },
    })
    .mobiscroll('getInst');

  var addEditPopup = $('#demo-add-edit-popup')
    .mobiscroll()
    .popup({
      display: 'bottom',
      contentPadding: false,
      fullScreen: true,
      scrollLock: false,
      responsive: {
        medium: {
          display: 'anchored',
          width: 510,
          fullScreen: false,
          touchUi: false,
        },
      },
    })
    .mobiscroll('getInst');

  $eventProgress.on('input', function () {
    eventProgress = $(this).val();
    $eventProgressLabel.text(eventProgress + ' %');
  });

  $eventTitle.on('input', function () {
    eventTitle = this.value;
  });

  $('#demo-task-progression')[0].addEventListener(
    'mousedown',
    function (event) {
      var $progressArrow = $(event.target).closest('.mds-progress-arrow');

      if (!$progressArrow.length) {
        return;
      }

      event.stopPropagation();

      var $progressBar = $progressArrow.closest('.mds-progress-bar');
      var eventContainerWidth = $progressBar.parent().width();
      var initialMouseX = event.pageX;
      var initialProgress = ($progressBar.width() / eventContainerWidth) * 100;

      function onMouseMove(e) {
        var mouseXOffset = e.pageX - initialMouseX;
        var newProgress = Math.round(initialProgress + (mouseXOffset / eventContainerWidth) * 100);

        eventProgress = Math.max(0, Math.min(100, newProgress));

        $progressBar.addClass('mds-progress-dragging').css('width', eventProgress + '%');

        $progressArrow
          .closest('.mds-progress-event')
          .find('.mds-progress-label')
          .text(eventProgress.toFixed(0) + '%');

        isDraggingProgress = true;
      }

      function onMouseUp() {
        $(document).off('mousemove', onMouseMove);
        $(document).off('mouseup', onMouseUp);

        setTimeout(function () {
          isDraggingProgress = false;
        }, 100);

        $progressBar.removeClass('mds-progress-dragging');

        var eventId = $progressArrow.data('event-id');
        var eventToUpdate = myEvents.find(function (event) {
          return event.id === eventId;
        });

        eventToUpdate.progress = eventProgress;
        calendar.updateEvent(eventToUpdate);
      }

      $(document).on('mousemove', onMouseMove);
      $(document).on('mouseup', onMouseUp);
    },
    true,
  );
});
```

### HTML

```html
  <div id="demo-task-progression"></div>
  <div style="display: none;">
    <div id="demo-add-edit-popup">
      <div class="mbsc-form-group">
        <label>
          Title
          <input mbsc-input class="mds-popup-event-title" />
        </label>
      </div>
      <div class="mbsc-form-group">
        <label>
          <input mbsc-input data-label="Starts" id="popup-event-start" />
        </label>
        <label>
          <input mbsc-input data-label="Ends" id="popup-event-end" />
        </label>
      </div>
      <div class="mbsc-form-group">
        <div class="mbsc-flex mbsc-align-items-center mbsc-padding">
          <label for="progress-slider">Progress</label>
          <input id="progress-slider" class="mds-popup-progress-slider mbsc-flex-1-0" type="range" min="0" max="100" />
          <span class="mds-popup-progress-label">0%</span> 
        </div>
        <div id="popup-event-dates"></div>
      </div>
    </div>
  </div>
```

### CSS

```css
.mds-progress-calendar .mbsc-timeline-row-gutter {
  height: 6px;
}

.mds-progress-calendar .mbsc-timeline-parent {
  height: 34px;  
}

.mds-popup-progress-slider {
  margin: 0 10px;
}

.mds-popup-progress-label {
  margin-left: 10px;
  width: 50px;
  text-align: right;
}

.mds-progress-event {
  border-radius: 4px;
  position: relative;
  overflow: hidden;
  line-height: 19px;
}

.mds-progress-event-content {
  padding: 10px 0;
  margin-right: 50px;
}

.mds-progress-event-title {
  position: relative;
  color: white;
  font-size: 14px;
  padding: 0 10px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.mds-progress-bar {
  position: absolute;
  top: 0;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
}

.mds-progress-arrow {
  position: absolute;
  right: -9px;
  bottom: 0;
  border-style: solid;
  border-width: 0 9px 9px 9px;
  border-color: transparent transparent rgba(255, 255, 255, 0.5) transparent;
  cursor: ew-resize;
}

.mds-progress-dragging {
  background-color: rgba(0, 0, 0, 0.5);
}

.mds-progress-dragging .mds-progress-arrow,
.mds-progress-arrow:hover {
  right: -12px;
  border-width: 0 12px 12px 12px;
  border-color: transparent transparent white transparent;
}

.mds-progress-label {
  position: absolute;
  right: 5px;
  top: 50%;
  margin-top: -10px;
  color: white;
  font-weight: bold;
  font-size: 14px;
}

.mds-progress-employee-name {
  font-size: 14px;
}

.mds-progress-employee-title {
  font-size: 12px;
  font-weight: 400;
  margin-top: 5px;
}
```

## Implementation notes

- Use `type: 'month'` with `eventDisplay: 'fill'` for a month-level Gantt-style view where tasks span multiple days.
- Give each event a custom `progress` property (0-100) representing task completion percentage.
- Define resources as a two-level tree of team groups (`eventCreation: false`) and individual members with a custom `title` (job role). Use `renderResource` (Angular: `resourceTemplate`, Vue: `#resource` slot) to show the member name and role beneath it.
- Use `renderTimelineEvent` (Angular: `timelineEventTemplate`, Vue: `#timelineEvent` slot) to render the progress bar layout: a container styled with the resource color, a progress fill div with `width` set to `${progress}%`, a draggable arrow handle element with a `data-event-id` attribute, the task title, and a percentage label.
- Implement inline progress dragging by attaching `onMouseDownCapture` on the calendar wrapper. On mousedown on the arrow handle, call `stopPropagation` to prevent Mobiscroll from treating it as an event click or drag, then attach `mousemove`/`mouseup` listeners to the document. On mousemove, compute new progress from the x-offset relative to the event container width. On mouseup, write the new value to the event in state and remove the listeners.
- Track drag state with an `isDraggingProgress` ref. In `onEventClick`, return early if `isDraggingProgress.current` is true so the edit popup does not open when the user finishes dragging the handle. Use a short `setTimeout` before resetting the ref to prevent the mouseup from immediately triggering a click.
- Enable `clickToCreate: true` and `dragToCreate: true` for event creation. Use `onEventCreated` to open the add popup with `args.target` as the anchor. Use `onEventClick` to open the edit popup with `args.domEvent.target` as the anchor. Use `onEventUpdated` to persist drag-to-move and drag-to-resize changes.
- Build the popup with `Popup`; default to full-screen bottom sheet on mobile and switch to an anchored 400px popup at the medium breakpoint.
- Include a `Datepicker` in range mode with `startInput`/`endInput` refs for editing the task date range, and a native `<input type="range">` (0-100) for updating the progress value directly in the form.
- On popup close when the add form is canceled, refresh the events list to remove the temporary placeholder event.

## 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: [Dual Synchronized Flight Scheduling](https://demo.mobiscroll.com/jquery/timeline/flight-scheduling-two-synchronized-timelines.md) - Use synchronized JQuery Timeline views to build a flight scheduling UI — drag flights to assign them to aircraft, with fixed event times and conflict detection.
- Next: [Shifts and Tasks](https://demo.mobiscroll.com/jquery/timeline/tasks-subtasks-under-shifts.md) - Display shifts and linked tasks on a JQuery Timeline — group tasks under parent shifts and restrict movement to each shift's time range.
