7

I'm using a template that I bought. If I click on a link to go to another page, the javascript is not executed again. That's a big problem since there are a lot of things happening in these js.

I don't know for what reason but the only time it's working is when I click on the nav menu. Maybe it's because the nav bar has an ID and is used in the js file.

Here is a simplified version of my main.js:

(function($) {

    var $window = $(window),
        $body = $('body'),
        $header = $('#header');

//...//
    // Play initial animations on page load.
        $window.on('load', function() {
            window.setTimeout(function() {
                $body.removeClass('is-preload');
            }, 100);
        });

    // Dropdowns.
        $('#nav > ul').dropotron({
            alignment: 'center'
        });

    // Off-Canvas Navigation.

        // Navigation Panel Toggle.
            $('<a href="#navPanel" class="navPanelToggle">Menu</a>')
                .appendTo($header);

        // Navigation Panel.
            $(
                '<div id="navPanel">' +
                    '<nav>' +
                        $('#nav').navList() +
                    '</nav>' +
                    '<a href="#navPanel" class="close"></a>' +
                '</div>'
            )
                .appendTo($body)
                .panel({
                    delay: 500,
                    hideOnClick: true,
                    hideOnSwipe: true,
                    resetScroll: true,
                    resetForms: true,
                    side: 'right',
                    target: $body,
                    visibleClass: 'is-menu-visible'
                });

})(jQuery);

And I just put it in my App.razor file:

<body class="is-preload">
    <!-- Wrapper -->
    <div id="wrapper">
        <Routes />
    </div>

    <script src="_framework/blazor.web.js"></script>
    <!-- Scripts -->
    <script src="template/js/jquery.min.js"></script>
    <script src="template/js/jquery.dropotron.min.js"></script>
    <script src="template/js/browser.min.js"></script>
    <script src="template/js/breakpoints.min.js"></script>
    <script src="template/js/util.js"></script>
    <script src="template/js/main.js"></script>
</body>

Can you help me. Thank you!

1
  • Can you show a simplified version of the JS that is being called, and show how it is currently being invoked on the first page load Commented Dec 2, 2023 at 13:04

1 Answer 1

5

DOM manipulation

The first thing to note with your code is that you are doing a lot of DOM manipulation with jQuery. You should try to do as much of your DOM manipulation from within Blazor as you can. The main reason is that Blazor manages the DOM based on the state of your components, so any changes you make outside of Blazor could be lost when the state changes in your Blazor components.

Relevant StackOverflow post: Blazor, doubts regarding modifying DOM from JavaScript

Blazor page loading

Blazor isn't like a traditional server rendered app. Regardless of which flavour of Blazor you are using (server / web assembly), you only load resources (e.g. JS, CSS) on the initial page load, much like any other SPA. This means that your JS will only run on the initial load - future navigations are made within the context of the original request.

As you are experiencing, JS will not automatically run per-navigation unless you specifically invoke it.

How to invoke JS from Blazor

Regardless of your strategy for invoking JS per-navigation, you need to understand how to call JS from your Blazor code.

Luckily Blazor gives us an easy way to do this.

Set up the javascript methods you want to run from Blazor.

(function() {
  function sayHello(name) {
    console.log(`Hello, ${name}!`);
  }

  // Create a namespace within the global scope that you can
  // add your own functions to.
  // You can only call JS functions from Blazor that are 
  // in the global scope
  window.myNamespace = window.myNamespace || {};
  // Add your function(s) to your scope here.
  // You may want to split different functions into different
  // files - hence why you get or create your namespace first.
  window.myNamespace.onLoad = {
    sayHello: sayHello
  };
)();

Call the JS function from a Blazor component. Blazor can only start interacting with JS after the first render. You can pass 0 or more arguments from Blazor into your JS function (including references to DOM elements, using the @ref Blazor binding).

@inject IJSRuntime JSRuntime
@code {
  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    if (firstRender)
    {
      // call the JS function
      // note how we don't need to explicitly call window.myNamespace
      await JSRuntime.InvokeVoidAsync("myNamespace.onLoad.sayHello", "World");
    }
  }
}

How to invoke JS on every page load

There are several approaches you could take here. One approach I might use would be to have a base component for your pages that can call the required functions on the first render.

This would mean that you have to remember to inherit from the base class when you create a new page.

Set up your base page component:

public abstract class BasePageComponent : ComponentBase
{

  [Inject]
  protected IJSRuntime JSRuntime {get; set;}

  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    await base.OnAfterRenderAsync(firstRender);

    if (firstRender)
    {
      // run some JS function(s) here 
      await JSRuntime.InvokeVoidAsync("myNamespace.onLoad.sayHello", "World");
    }
  }
}

Simply set up your pages to inherit from this base class like so:

@page "/"
@inherits BasePageComponent

There are undoubtedly other ways in which you could run JS per-navigation. My answer will at least arm you with the ability to call JS from Blazor on page load, and should hopefully inspire you to re-think your current reliance on jQuery.

Sign up to request clarification or add additional context in comments.

4 Comments

"The first thing to note with your code is that you are doing a lot of DOM manipulation with jQuery" I agree, but the thing is that it's javascript provided by the template. If I need to replace all the JS of the template than maybe I should not use any template at all.
In that case I would suggest that the template could be improved upon. But that's out of scope for what you want to achieve. You are mostly interested in being able to trigger JS per navigation. Does my post help you achieve that, or do you need more help?
I was searching for a solution to fix it and keep the template js. But like you explain, thank you a lot for that, it seems like trying to execute js per navigation is a bad idea at the end. I'm gonna try to replace the JS by C# even if this will take time.
I can understand why you've started off with a template - nothing wrong with that. I wouldn't take too much guidance about Blazor best practices from it though - view it more as a quick and easy start. I can vouch for BlazorStrap (Bootstrap) and MudBlazor (Material) for native Blazor component libraries. I'm sure more will surface as Blazor (hopefully) becomes more popular.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.