Search code examples
c#blazor.net-6.0

How to close a popup in Blazor without using a cross button?


In my Blazor project I use a popup.

  • I want to close my popup by clicking next to the popup.
  • I don't have a cross on my popup.
  • And I don't want to use a Cancel button.

How do you do that?

This is the popup:

<div class="modal @modalClass" 
     tabindex="-1" role="dialog" 
     style="display:@modalDisplay; overflow-y: auto;">
    <div class="modal-dialog modal-lg" role="document" >
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">@Title</h5>
            </div>
            <div class="modal-body">
                @Body
            </div>
            <div class="modal-footer">
                @Footer
            </div>
        </div>
    </div>
</div>

@if (showBackdrop)
{
    <div class="modal-backdrop fade show"></div>
}

@code {
    [Parameter]
    public RenderFragment Title { get; set; }

    [Parameter]
    public RenderFragment Body { get; set; }

    [Parameter]
    public RenderFragment Footer { get; set; }

    private string modalDisplay = "none;";
    private string modalClass = "";
    private bool showBackdrop = false;

    public void Open()
    {
        modalDisplay = "block;";
        modalClass = "show";
        showBackdrop = true;
    }

    public void Close()
    {
        modalDisplay = "none";
        modalClass = "";
        showBackdrop = false;
    }
}

... and this will be called in a Razor page.

<ModalPage @ref="_modal">
    <Title>Some title</Title>
    <Body>
        <div class="row">
            <div class="col">some number</div>
            <div class="col">@_model.SomeNumber</div>
        </div>

Solution

  • Edit:

    There is a better solution that don't require using any JavaScript. Simply add @onclick event on .modal element and then add @onclick:stopPropagation on the .modal-dialog element so that clicks inside the modal dialog won't trigger the click event on the parent:

    <div class="modal @modalClass" 
         tabindex="-1" role="dialog" 
         style="display: @modalDisplay; overflow-y: auto;"
         @onclick="Close">
        <div class="modal-dialog modal-lg" role="document" @onclick:stopPropagation="true">
            ...
        </div>
    </div>
    

    Original answer:

    I modified your ModalPage component so that it closes when the user clicks anywhere outside the modal:

    @inject IJSRuntime JS
    @implements IDisposable
    
    <div class="modal @modalClass" 
         tabindex="-1" role="dialog" 
         style="display: @modalDisplay; overflow-y: auto;">
        <div class="modal-dialog modal-lg" role="document" >
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">@Title</h5>
                </div>
                <div class="modal-body">
                    @Body
                </div>
                <div class="modal-footer">
                    @Footer
                </div>
            </div>
        </div>
    </div>
    
    @if (showBackdrop)
    {
        <div class="modal-backdrop fade show"></div>
    }
    
    @code {
        [Parameter]
        public RenderFragment Title { get; set; }
    
        [Parameter]
        public RenderFragment Body { get; set; }
    
        [Parameter]
        public RenderFragment Footer { get; set; }
    
        private string modalDisplay = "none";
        private string modalClass = "";
        private bool showBackdrop = false;
    
        private DotNetObjectReference<ModalPage> _selfRef;
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                _selfRef = DotNetObjectReference.Create(this);
                await JS.InvokeVoidAsync("initModal", _selfRef);
            }
        }
    
        public void Open()
        {
            modalDisplay = "block";
            modalClass = "show";
            showBackdrop = true;
        }
        
        [JSInvokable]
        public void Close()
        {
            modalDisplay = "none";
            modalClass = "";
            showBackdrop = false;
    
            StateHasChanged();
        }
    
        public void Dispose()
        {
            _selfRef.Dispose();
        }
    }
    

    You also need to add this script inside index.html (or _Layout.cshtml):

    <script>
        window.initModal = function (dotNetHelper) {
            document.querySelector('.modal').addEventListener('click', function (e) {
                // Check if the modal is clicked, not an element inside the modal:
                if (e.target === e.currentTarget) {
                    dotNetHelper.invokeMethodAsync('Close');
                }
            });
        };
    </script>
    

    BlazorFiddle

    Detect click on bootstrap modal background