---
title: "A Simple To-Do App Using JS Interop with HTML5 Drag and Drop in Blazor WebAssembly"
published_at: "2022-12-19T11:30:26+00:00"
modified_at: "2025-11-06T11:26:11+00:00"
url: "https://www.syncfusion.com/blogs/post/to-do-app-using-js-interop-with-html5-drag-and-drop-in-blazor-webassembly"
excerpt: "I hope you know that Microsoft rejected offering built-in support for drag-and-drop in Blazor. You can learn more about this denial in Visual Studio Magazine and the GitHub issue. This blog will implement a simple to-do Blazor WebAssembly app using..."
taxonomy_category:
  - "Blazor"
  - "Development"
  - "HTML"
  - "JavaScript"
  - "Web"
taxonomy_post_tag:
  - "Blazor"
  - "HTML5"
  - "JavaScript"
  - "Web Development"
  - "WebAssembly"
---

# A Simple To-Do App Using JS Interop with HTML5 Drag and Drop in Blazor WebAssembly

[Rajendran R](https://www.syncfusion.com/blogs/author/rajendranr)

![A Simple To-Do App Using JS Interop with HTML5 Drag and Drop in Blazor WebAssembly](https://www.syncfusion.com/blogs/wp-content/uploads/2022/12/A-Simple-To-Do-App-Using-JS-Interop-with-HTML5-Drag-and-Drop-in-Blazor-WebAssembly.png)


I hope you know that Microsoft rejected offering built-in support for drag-and-drop in Blazor. You can learn more about this denial in [Visual Studio Magazine](https://visualstudiomagazine.com/articles/2022/03/30/blazor-drag-drop.aspx)
 and the [GitHub issue](https://github.com/dotnet/aspnetcore/issues/18754)
.

This blog will implement a simple to-do Blazor WebAssembly app using the HTML5 drag-and-drop API and Syncfusion [Blazor Card](https://www.syncfusion.com/blazor-components/blazor-card)
 and [Accordion](https://www.syncfusion.com/blazor-components/blazor-accordion)
 components via JavaScript interop. In the app, we will implement the following functionalities:

- Drag and drop tasks within the to-do section.
- Drag and drop tasks to the completed section.
- Mark a task as done on the change to the radio button.

## Getting started with Blazor WASM

To get started with a Blazor WebAssembly app, refer to the [Blazor documentation](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio)
.

## Create a Task Model class

Create a model class named **TaskModel** with the properties ** Id**,** Summary**,** Status**, and ** Timeline** to manage the tasks.

Create a **TaskStatuses** enum with the members ** Todo** and ** Completed**.

Refer to the following code:

```
namespace Blazor_WASM_Drag_and_Drop.Models
{
    public class TaskModel
    {
        public int Id { get; set; }
        public TaskStatuses Status { get; set; }
        public string Summary { get; set; } = String.Empty;
        public DateTime Timeline { get; set; }
    }

    public enum TaskStatuses
    {
        Todo,
        Completed
    }

}
```

Initialize the data based on the model class in **OnInitializedAsync**.

```
@code
{
    public static List<TaskModel> Tasks = new List<TaskModel>();

    protected override void OnInitialized()
    {
        InitTasks();
    }

    public void InitTasks()
    {
        Tasks.Add(new TaskModel
            {
                Id = 1,
                Summary = "Planning Tasks for sprint",
                Status = TaskStatuses.Todo,
                Timeline = new DateTime(2022, 04, 09)
            });
        …
    }
}
```

## Enable drag and drop for tasks

Create a **TaskDetail** component using the Syncfusion Blazor Card.

To learn more about integrating the Syncfusion Blazor Card in a Blazor application, refer to its [documentation](https://blazor.syncfusion.com/documentation/card/getting-started)
.

Refer to the following code to bind the draggable attribute.

```
<SfCard>
    <CardContent>
        <div class="taskitem" id="@TaskModel.Id.ToString()" draggable="@IsDraggable">
            <div class="taskstate">
                <SfRadioButton Value="@TaskModel.Status.ToString()" Checked="IsCompleted"></SfRadioButton>
            </div>
            <div class="taskcontent">
                <div class="tasksummary @TaskModel.Status.ToString().ToLower()"> @TaskModel.Summary </div>
                <div class="tasktimeline"> @TaskModel.Timeline.ToLongDateString() </div>
            </div>
        </div>
    </CardContent>
</SfCard>

@code {
    string IsDraggable = string.Empty;
    string IsCompleted = "Completed";

    [Parameter]
    public TaskModel TaskModel { get; set; }

    protected override void OnInitialized()
    {
        IsDraggable = TaskModel.Status == TaskStatuses.Todo ? "true" : "false";
    }
}
```

## HTML5 drag-and-drop APIs in C#

Blazor supports the C# representation of drag-and-drop events. You can find details about it in the [ASP.NET Core Blazor event handling documentation](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-6.0)
.

![ASP.NET Core Blazor event handling](https://www.syncfusion.com/blogs/wp-content/uploads/2022/12/ASP.NET-Core-Blazor-event-handling-documentation.png)

**Source:** [ASP.NET Core Blazor event handling](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-6.0)

## ToDo component with drag-and-drop events

Create a **ToDo** component and bind drag-and-drop-related events to the root elements. Also, define C# functions for the following actions:

- By default, the drop action is not allowed. So, set **preventDefault** as ** true** in ** ondragover and dragenter**events.
- Include child component **TaskDetail** by passing ** TaskModel** as a parameter.

Refer to the following code.

```
@page “/”

@using Syncfusion.Blazor.Navigations
@inject IJSRuntime JSInterop

<PageTitle>A simple To-Do App using Blazor WebAssembly</PageTitle>

<div class=”droptarget” @ondragstart=”DragStart” @ondragover=”DragOver”
     @ondragover:preventDefault=”shouldPreventDefault” @ondragenter:preventDefault=”shouldPreventDefault”
     @ondragleave=”DragLeave” @ondrop=”DropCard” @ondrop:preventDefault=”shouldPreventDefault”>

    @foreach (var task in Tasks.FindAll(c => c.Status == TaskStatuses.Todo))
    {
        <CascadingValue Value=”this”>
            <TaskDetail TaskModel=”task”></TaskDetail>
        </CascadingValue>
    }

</div>

@code
{
    private bool shouldPreventDefault = true;

    private void DragOver(System.EventArgs e)
    {
    }

    private void DragStart(System.EventArgs e)
    {
    }

    private void DragEnter(System.EventArgs e)
    {
    }

    private void DragLeave(System.EventArgs e)
    {
    }

    private void DropCard(System.EventArgs e)
    {
    }
}
```

## JavaScript interop

Pass the data from the server to the client and the client to the server through JS interop.

- Inject **IJSRuntime** in the ** ToDo**component.
- Call a JavaScript method using the **InvokeVoidAsync** method from ** ondragstart** and ** ondrop** to get the dragged task ID and the dropped task ID.
- The server method must be the **JSInvokableAttribute** attribute.

Refer to the following code.

```
@page "/"

@using Syncfusion.Blazor.Navigations
@inject IJSRuntime JSInterop

<PageTitle>A simple To-Do App using Blazor WebAssembly</PageTitle>

<div class="droptarget" ondragstart="DragStart(event,event.target.id);" @ondragover="DragOver"
     @ondragover:preventDefault="shouldPreventDefault" @ondragenter:preventDefault="shouldPreventDefault"
     @ondragleave="DragLeave" @ondrop="DropCard" @ondrop:preventDefault="shouldPreventDefault">

    @foreach (var task in Tasks.FindAll(c => c.Status == TaskStatuses.Todo))
    {
        <CascadingValue Value="this">
            <TaskDetail TaskModel="task"></TaskDetail>
        </CascadingValue>
    }

</div>

@code
{
    private bool shouldPreventDefault = true;
    public static List<TaskModel> Tasks = new List<TaskModel>();

    private void DragOver(System.EventArgs e)
    {
    }

    private async Task DragStart(System.EventArgs e, string Id)
    {
        await JSInterop.InvokeVoidAsync("DragStart", e, Id);
    }

    private void DragEnter(System.EventArgs e)
    {
    }

    private void DragLeave(System.EventArgs e)
    {
    }

    private async Task DropCard(System.EventArgs e)
    {
        await JSInterop.InvokeVoidAsync("DropCard", e);
    }

    [JSInvokableAttribute("UpdateCards")]
    public static void UpdateCardsAfterDrop(bool IsCompleted, string dragTarget, string dropTarget)
    {
    }
}
```

Create a JavaScript file, **DragAndDrop.js**, within the ** wwwroot** folder and add it to the ** index.html** file.

```
<!DOCTYPE html>
<html lang="en">

<head>
  …
  <script src="DragAndDrop.js"></script>
</head>

<body>
</body>

</html>
```

Include the following code in the **DragAndDrop.js** file.

```
var dragCardId = null;

function DragStart(args, id) {
    dragCardId = id;
}

function DropCard(args) {
    var dropElement = document.elementFromPoint(args.clientX, args.clientY);
    var IsDroppedOnCompletedPane = dropElement.closest('.e-accordion') ? true : false;
    var dropCard = dropElement.closest('.taskitem');
    DotNet.invokeMethodAsync('Blazor_WASM_Drag_and_Drop', 'UpdateCards', IsDroppedOnCompletedPane, dragCardId, dropCard != null ? dropCard.id : null);
}
```

In the above code, we:

- Get the drag task ID in the drag start event through its parameter which is passed from C# to JavaScript.
- Fetch the drop task IDd using **elementFromPoint** in the drop event.
- Call the C# method **UpdateCards** from JavaScript by invoking the ** DotNet.invokeMethodAsync** method.

## Reorder tasks within to-do items using drag and drop

Reorder the tasks by getting the corresponding index based on its ID.

```
[JSInvokableAttribute("UpdateCards")]
    public static void UpdateCardsAfterDrop(bool IsCompleted, string dragTarget, string dropTarget)
    {
        int dragIndex = Tasks.IndexOf(Tasks.Single(d => d.Id == int.Parse(dragTarget)));
        int dropIndex = Tasks.IndexOf(Tasks.Single(d => d.Id == int.Parse(dropTarget)));

        if (dropIndex != -1)
        {
            TaskModel task = Tasks[dragIndex];
            Tasks.RemoveAt(dragIndex);
            Tasks.Insert(dropIndex, task);
        }
    }
```

In the above code, we:

- Store the dragged task in a temporary variable.
- Remove the task from the dragged position.
- Append the dragged task at the dropped position.

## Update ‘Mark as Done’ when dragged and dropped

We include the Accordion component within the **ToDo** component to list the completed tasks. Learn more about the Syncfusion Blazor Accordion component from the [getting started documentation](https://blazor.syncfusion.com/documentation/accordion/getting-started)
.

When dragging a task from the to-do section and dropping it within the Accordion pane, update the dragged task status as **Completed** based on its index.

```
[JSInvokableAttribute("UpdateCards")]
public static void UpdateCardsAfterDrop(bool IsCompleted, string dragTarget, string dropTarget)
{
   int dragIndex = Tasks.IndexOf(Tasks.Single(d => d.Id == int.Parse(dragTarget)));
   Tasks[dragIndex].Status = TaskStatuses.Completed;
}
```

## ‘Mark as Done’ on change to radio button value

Mark the task as completed after clicking on the radio button within the to-do pane.

```
<SfCard>
    <CardContent>
        <div class="taskitem" id="@TaskModel.Id.ToString()" draggable="@IsDraggable">
            <div class="taskstate">
                <SfRadioButton Value="@TaskModel.Status.ToString()" Checked="IsCompleted" ValueChange="@((ChangeArgs<string> s) => OnTaskStatusChange(s))"></SfRadioButton>
            </div>
            <div class="taskcontent">
                <div class="tasksummary @TaskModel.Status.ToString().ToLower()"> @TaskModel.Summary </div>
                <div class="tasktimeline"> @TaskModel.Timeline.ToLongDateString() </div>
            </div>
        </div>
    </CardContent>
</SfCard>

@code {
…

    private void OnTaskStatusChange(ChangeArgs<string> args)
    {
        TaskParent.OnTaskStatusChanged(TaskModel);
        args.Value = "Completed";
    }

    [CascadingParameter]
    public ToDo TaskParent { get; set; }
}
```

In this code, we:

- Bind the **ValueChange** event to ** SfRadioButton**.
- Define parent component **ToDo** with ** CascadingParameter**.
- Pass the selected value from the child component (**TaskDetail**) to the parent component (** ToDo**) by calling the parent’s ** OnTaskStatusChanged**

In the **ToDo** component, define the method ** OnTaskStatusChanged** to receive a selected value from the child component (** TaskDetail**).

```
@code
{
…

    public void OnTaskStatusChanged(TaskModel task)
    {
        Tasks.FirstOrDefault(task).Status = TaskStatuses.Completed;
        StateHasChanged();
    }
}
```

Render a child component **TaskDetail** within the ** CascadingValue** component.

```
@page "/"

@using Syncfusion.Blazor.Navigations
@inject IJSRuntime JSInterop

<PageTitle>A simple To-Do App using Blazor WebAssembly</PageTitle>

<div class="droptarget" ondragstart="DragStart(event,event.target.id);" @ondragover="DragOver"
     @ondragover:preventDefault="shouldPreventDefault" @ondragenter:preventDefault="shouldPreventDefault"
     @ondragleave="DragLeave" @ondrop="DropCard" @ondrop:preventDefault="shouldPreventDefault">

    @foreach (var task in Tasks.FindAll(c => c.Status == TaskStatuses.Todo))
    {
        <CascadingValue Value="this">
            <TaskDetail TaskModel="task"></TaskDetail>
        </CascadingValue>
    }

</div>

@code
{
  …

  public void OnTaskStatusChanged(TaskModel task)
  {
     Tasks.FirstOrDefault(task).Status = TaskStatuses.Completed;
     StateHasChanged();
  }
}
```

Now the application is ready. Refer to the following GIF image to see what the application should look like.

![Creating a To-Do App Using JS Interop with HTML5 Drag and Drop in Blazor WebAssembly](https://www.syncfusion.com/blogs/wp-content/uploads/2022/12/Creating-a-To-Do-App-Using-JS-Interop-with-HTML5-Drag-and-Drop-in-Blazor-WebAssembly.gif)

Creating a To-Do App Using JS Interop with HTML5 Drag and Drop in Blazor WebAssembly

## Resources

You can access the complete project in the [to-do app using JS interop with HTML5 drag and drop in Blazor WebAssembly](https://github.com/SyncfusionExamples/blazor-drag-and-drop-todo)
 GitHub repository.

## Conclusion

In this blog post, we have seen how to create a simple to-do app in Blazor WebAssembly using the JS interop with HTML5 drag-and-drop feature. I hope you find this blog useful.

Syncfusion’s [Blazor](https://www.syncfusion.com/blazor-components)
 component suite offers over 70 UI components. They work with both server-side and client-side (WebAssembly) hosting models seamlessly. Use them to build marvelous apps!

If you have any questions or comments, you can contact us through our [support forum](https://www.syncfusion.com/forums)
, [support portal](https://support.syncfusion.com/)
, or [feedback portal](https://www.syncfusion.com/feedback/)
. We are always happy to assist you!

## Related blogs

If you like this blog, we think you will enjoy the following Blazor articles as well:

- [CRUD Operations Using Blazor and MongoDB [Webinar Show Notes]](https://www.syncfusion.com/blogs/post/crud-operations-using-blazor-and-mongodb-webinar-show-notes.aspx)
- [Download Files from Azure Blob Storage with Blazor WebAssembly and ASP.NET Web API](https://www.syncfusion.com/blogs/post/download-files-from-azure-blob-storage-with-blazor-webassembly-and-asp-net-web-api.aspx)
- [A Full-Stack Web App Using Blazor WebAssembly and GraphQL—Part 7](https://www.syncfusion.com/blogs/post/a-full-stack-web-app-using-blazor-webassembly-and-graphql-part-7.aspx)
- [Easily Use a Dynamic Blob Container in the Blazor File Manager](https://www.syncfusion.com/blogs/post/easily-use-a-dynamic-blob-container-in-the-blazor-file-manager.aspx)
