Search code examples
c#razorblazorwebassemblyblazor-webassembly

How can I reach body tag or another tag from Razor Page in Blazor WebAssembly?


I'm trying to reach to body from a Razor page. I have just started to learn Blazor. So I don't know if it is possible or not. Basically I'm trying to change the body class from the C# side and from different pages. I can do it with JavaScript but I want to do it with Blazor WebAssembly. It can work if the body is inside of the Razor page. But I don't want to use it that way.

index.html:

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="utf-8" />
</head>
<body class="theme-dark">
    <app>Loading...</app>
    <script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

MainLayout.razor:

@inherits LayoutComponentBase
<div class="grid-wrapper sidebar-bg bg1">
    <div id="theme-tab">
        <div class="theme-tab-item switch-theme bg-white" @onclick="ToggleTheme" data-theme="theme-default" title="Light"></div>
        <div class="theme-tab-item switch-theme bg-dark" @onclick="ToggleTheme" data-theme="theme-dark" title="Dark"></div>
    </div>
    <NavMenu />
    <SideBar />
    <div class="main">
        @Body
    </div>
    <Footer />
</div>

@code {
    private bool isDark = true;
    private string currentTheme => isDark ? "theme-dark" : "theme-default"; //I want to use this variable in other pages.

    private void ToggleTheme()
    {
        isDark = !isDark;
    }
}

Solution

  • If you want to change out the <body> class

    Add a JS file.

    site.js

    window.SetBodyCss = function (elementId, classname) {
        var link = document.getElementById(elementId);
        if (link !== undefined) {
            link.className = classname;
        }
        return true;
    }
    

    Reference it in Host.cshtml.
    Create an id on <body>.

    <body id="BlazorMainBody">
    
        <script src="/site.js"></script>
        <script src="_framework/blazor.server.js"></script>
    </body>
    

    Add a helper class

    using Microsoft.JSInterop;
    using System.Threading.Tasks;
    
    namespace Blazor.Starter
    {
        public class AppJsInterop
        {
            protected IJSRuntime JSRuntime { get; }
    
            public AppJsInterop(IJSRuntime jsRuntime)
            {
                JSRuntime = jsRuntime;
            }
    
            public ValueTask<bool> SetBodyCss(string elementId, string cssClass)
              => JSRuntime.InvokeAsync<bool>("SetBodyCss", elementId, cssClass);
        }
    }
    

    This shows toggling it in a page

    <button type="button" @onclick="SetCss">SetCss</button>
    
    @code {
        [Inject] private IJSRuntime _js { get; set; }
    
        private bool _isbodyCss;
    
        private string _bodyCss => _isbodyCss ? "Theme-Dark" : "Theme-Light";
    
        private async Task SetCss()
        {
            var appJsInterop = new AppJsInterop(_js);
            await appJsInterop.SetBodyCss("BlazorMainBody", _bodyCss);
            _isbodyCss = !_isbodyCss;
        }
    }
    

    You can change out the whole stylesheet in a similar way - see this note of mine