Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net ScriptManager causing issues with jQuery Widget

I've been using ScriptManager and the jQuery slider widget together on sites for years, but I recently came up against an issue which stopped the widget from working. I managed to resolve the problem, but it was more out of luck than expertise. I'm hoping that someone can provide reasoning behind the issue and that the fix might prove useful to others with the same issue.

I use a script aggregator which combines my scripts together - here is what it contains - all code snippets have been paired down for brevity:

vendor/Modernizr.min.js
vendor/jQuery.3.0.0.min.js
vendor/jQuery-UI.1.12.1.min.js
vendor/jQuery-UI.TouchPunch.min.js
propriertary/LoanSelector.js
DefaultInit.js

Here is the contents of DefaultInit.js:

$(document).ready(function () {
    loanSelector.init();
});

Here are the bare bones of LoanSelector.js

var loanSelector = function () {

    var pub = {}, $ctl = $("#loan-selector"),
        $sliderAmount = $ctl.find(".slider-amount:first"),
        $widgetAmount = $sliderAmount.find(".widget:first");

    function initControl() {
        initWidgetAmount(100, 2000, 100, $("#hfStartAmount").val());
    }

    function initWidgetAmount(min, max, step, initialVal) {
        $widgetAmount.slider({
        });
    }

    pub.init = function () {
        initControl();
    };

    return pub;
} ();

Now this works - but only without ScriptManager on the page.

When script manager is added, the slider widget doesn't load - no errors - nothing.

The cached elements are permanent fixtures on the page - so it's not a case of the elements not existing at a point in time.

Here's the ScriptManager code:

    <asp:ScriptManager ID="SM" runat="server"
        EnableCdn="true"
        LoadScriptsBeforeUI="false"
        EnablePageMethods="true">
    </asp:ScriptManager>

If I make changes, I can get the widget to load with the ScriptManager on the page - here's another version of LoanSelector.js that works:

var loanSelector = function () {

    var pub = {}, $ctl, $sliderAmount, $widgetAmount;

    function cacheElements() {
        $ctl = $("#loan-selector"),
        $sliderAmount = $ctl.find(".slider-amount:first"),
        $widgetAmount = $sliderAmount.find(".widget:first");
    }

    function initControl() {
        // This is new
        cacheElements();

        initWidgetAmount(100, 2000, 100, $("#hfStartAmount").val());
    }

    function initWidgetAmount(min, max, step, initialVal) {
        $widgetAmount.slider({
        });
    }

    pub.init = function () {
        initControl();
    };

    return pub;
} ();

So, can anyone throw some light on why it might not be working and why the fix does work?

like image 288
John Ohara Avatar asked Oct 03 '17 10:10

John Ohara


1 Answers

The first method populates variables as it executes:

var pub = {}, $ctl = $("#loan-selector"),
    $sliderAmount = $ctl.find(".slider-amount:first"),
    $widgetAmount = $sliderAmount.find(".widget:first");

So #loan-selector, .slider-amount:first and .widget:first need to be rendered in the page's DOM before this is called.

The second variant populates when loanSelector.init() is called, and by the place in your code where you call it the DOM is loaded.

<asp:ScriptManager> gives you a server side collection of scripts controls can add to easily, but it renders to a list of <script> tags and order can be important - another control in the page can mean that the DOM you're relying on hasn't loaded by the time your code runs. Scripts run in order but don't wait for the DOM render to finish.

jQuery has it's own component structure you can use instead, and a DOM ready method that you can rely on - you're already using it in DefaultInit.js, which is why the loanSelector.init() can access the DOM while the directly executed code does not.

You already have a fix, and your second script is better practice because it's not dependent on time of execution.

Another alternative would be to call your loanSelector function in the $(document).ready:

$(function(){
    // In this DOM ready function your elements should be ready to find
    var loanSelector = ...
    ...

    // And you may not need this, as you can init in the actual function
    loanSelector.init();
});
like image 157
Keith Avatar answered Nov 12 '22 21:11

Keith