This is a migrated thread and some comments may be shown as answers.

Kendo initialization scripts in body interfere with other libraries

29 Answers 2266 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Matt
Top achievements
Rank 1
Matt asked on 10 Jul 2012, 02:11 PM
Ok, it seems that there is a problem with the way that the MVC extensions render their javascript and I need some Admin input in order to resolve this going forward as it's causing massive issues.

With the lack of the script registrar, the MVC extensions appear to dump thier javascript in the middle of the body, immediately after the element being modified. The outputted javascript is wrapped in an auto-running function so that it executes immediately. Apart from dumping js into the middle of the page, which is by popular opinion considered a dubious practice, this causes problems with other javascript libraries that may then go in afterwards and manipulate the DOM. If such a library snips out the parent of an element in question which happens to contain the associated script tags and reinserts it, all of the javascript snippets run again, causing the Kendo controls to break (because they have been initialised twice) and everything to fail. If the Kendo scripts were at the end of the body this wouldn't be an issue.

Why isn't all javascript rendered at the end of the body? How can this be resolved?

29 Answers, 1 is accepted

Sort by
0
Atanas Korchev
Telerik team
answered on 10 Jul 2012, 03:28 PM
Hi Matt,

 Currently Kendo UI widgets output their JavaScript in a $(function() {} ) block. This means that it will execute when the DOM ready event is raised. If you want to modify the DOM before that you can attach your handler before the declaration of the Kendo widgets:

<script>
$(function() {
    // modify dom
});
</script>
 
@(Html.Kendo().DatePicker())

 There is no way right now to prevent the widgets from rendering their initialization JavaScript as without it nothing will work. 

Regards,

Atanas Korchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Matt
Top achievements
Rank 1
answered on 10 Jul 2012, 03:41 PM
The problem isn't so much the order of execution, it's the placement of Kendo's javascript. If I remove a DOM element that contains Kendo elements in my document ready handler and reinsert them, the Kendo handlers get attached to the document ready twice, because in effect they have been added to the DOM twice.

Say I insert a dropdown using DropDownFor into a div. This is the rendered HTML output:

<div class="editor-field">
    <span class="k-widget k-dropdown k-header" tabindex="0" style="" unselectable="on">
    <script>
        jQuery(function(){jQuery("#MyField_Something").kendoDropDownList();});
    </script>
    <span class="k-invalid-msg" data-for="MyField.Something"></span>
</div>

If I then remove this div from the DOM and reinsert it, the Kendo handler will get attached twice, once when the document first loads and a second when I reinsert it. This causes problems - in the case of the dropdown list, it disappears.
0
Atanas Korchev
Telerik team
answered on 10 Jul 2012, 03:48 PM
Hi Matt,

 Can you exclude the <script> elements when moving the DOM nodes? If they have been executed already you can just remove them before performing the move:

$("<some selector>").find("script").remove().end().appendTo($("<some other selector"));

 What is the code used to move the DOM elements?

Regards,
Atanas Korchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Matt
Top achievements
Rank 1
answered on 10 Jul 2012, 03:58 PM
Thanks for the sample, indeed, that is what I have done up until now - although I still have an issue with an editor iframe, however I have been able to work around that in other ways. In order to move the dom elements I've simply used jQuery's append function, similar to the code you've provided.

My concern with this is that it seems hacky rather than a real fix. It's also a pain to do if the DOM manipulation is performed by a third party library that's been minified - I can't edit their source and am left trying to hook in my event at the correct point in time to intercept the move. On top of that, in the interests of clean HTML/script I'm sure I'm not the only dev who would prefer to see this script down at the bottom of the rendered body rather than splattered throughtout the DOM. Are there any plans to have the scripts render in a similar fashion to the old extensions?
0
Atanas Korchev
Telerik team
answered on 10 Jul 2012, 04:03 PM
Hello Matt,

 We currently don't have plans to revive the ScriptRegistrar. We could probably however investigate if we can use Razor sections to output the script in a user specified location. I am not sure if this is even possible but is worth checking.

Regards,
Atanas Korchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Matt
Top achievements
Rank 1
answered on 10 Jul 2012, 04:12 PM
Hi Atanas,

Thanks, I certainly think this is worth investigating. I'm not too bothered about the script registrar being revived but I do think it would be nice to be able to instruct Kendo to render it's scripts at the bottom of the body. One of the nice things about the old extensions was that rendered HTML was quite clean and I think that should be a key selling point of Kendo as well.

Asthetics aside, I guess it's questionable whether this can be called a 'bug', but I do think the DOM manipulation issue will cause other users similar problems down the road. This is the first form I've tried to build with Kendo (albeit a rather complex one with an integrated jQuery wizard) and while it works when I use the old Telerik extensions, it doesn't with Kendo. The behaviour should at least be equivalent to allow easy transition for existing users.

For me I feel this is a deal breaker when it comes to adoption of the new toolset. Is there any way that I can track progress on this issue?
0
Matt
Top achievements
Rank 1
answered on 11 Jul 2012, 10:58 AM
Unfortunately I've had to come to the conclusion that this just hasn't been very well thought out and the extensions just aren't ready for real world use yet. I've had so much trouble with this that in the meantime I've written my own wrappers for the controls. The wrappers are thin and delegate internally to standard HTML helpers to create the HTML elements and they output all of the initialization javascript at the bottom of the body.  This solves all the aforementioned problems, including problems with validation attributes not being applied as the work is just being delegated to the standard Html helpers that have already been proven in a production environment. In the meantime I'll watch the official releases for a change in direction.
0
Atanas Korchev
Telerik team
answered on 11 Jul 2012, 11:39 AM
Hello,

 Do you mind sharing with us how you achieved rendering of the script at the bottom of your page? 

All the best,
Atanas Korchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Matt
Top achievements
Rank 1
answered on 11 Jul 2012, 11:55 AM

Hi Atanas,

Sure. I know we mentioned Razor sections earlier but I'm pretty sure that wouldn't work for EditorTemplates. In the end I've gone in a similar direction to the old school ScriptRegistrar and just recreated a lightweight version of that class. It's not a fully blown ScriptRegstrar or anything, it's merely an internal class that exposes a method that allows scripts to be registered (with a key) during processing of a view (I actually already had this class available as I use it to register JavaScript snippets inside reusable PartialViews). The thin HTML helpers I've created use this class internally to add the initialization scripts which stores them in the WebViewPage's context items collection and I have a single line of code at the end of my layout that requests the ScriptRegistrar to render those scripts.

Cheers.
0
LessX
Top achievements
Rank 1
answered on 16 Jul 2012, 09:08 PM
I render my reference of jquery bottom of the body.
I created a section for rendering my scripts bottom of my jquery reference,
my problem is, the scripts generate by kendo.mvc, render after my reference of jquery,
when I put it jquery on head then "columns.Template" not work.
0
Chris
Top achievements
Rank 1
answered on 08 Oct 2012, 03:35 PM
Hi Matt,

Is your solution available online anywhere?  I'm really keen to get this into our project to avoid the kind of problems you've had.

Many thanks,
Chris
0
Schalk
Top achievements
Rank 1
answered on 22 Nov 2012, 09:07 AM
I have posted a solution on PasteBin (http://pastebin.com/8pCUUv5e).

Basically what the code does:
HtmlHelper Extensions
Defer()
RenderDeferredScripts()

Defer gets passed a function that passes the HtmlHelper and expect an object in return.
public static MvcHtmlString Defer<TModel>(this HtmlHelper<TModel> htmlHelper, Func<HtmlHelper<TModel>, object> action)

RenderDeferredScripts should be called after your other scripts has loaded
public static MvcHtmlString RenderDeferredScripts(this HtmlHelper htmlHelper)

The defer method renders the action given the html helper, the result string is then passed through to the helper method ExtractScript
that splits up the html and script. The extracted script/s are then loaded into the HttpContext.Items (persists unlike the ViewBag), with the scripts inserted at position 0 (to keep the ordering top-down so that layout-*layouts-view-partialview scripts are rendered in order).

It's a bit of workaround, ideally the mvc extensions would just render the html and defer the script/s in a similar way as demonstrated here.

Sample usage:
View:
@Html.Defer
(html => html.Kendo().DropDownListFor(p => p.SalutationId).BindTo(Model.Salutations).HtmlAttributes(new { @class = "width-100" }))

Layout:
 @Scripts.Render("~/public/siteJs")
 @Html.RenderDeferredScripts()
0
Matt
Top achievements
Rank 1
answered on 22 Nov 2012, 09:25 AM
Hi Schalk,
Essentially that's what I did, however I noticed problems with this approach when using Kendo editors within a Kendo grid. In that scenario the editor controls broke because of the way that the MVC extensions render the JS and the way that Kendo's client side code works; it literally reinitializes the editors every time you put the grid into edit mode and it expects the scripts to be rendered inline. I never got around this issue and decided it was too much effort to maintain my patch. I instead modified the way my app worked. Disappointing and a rather large oversight on Kendo's part which I can't see being fixed unfortunately.
0
Rosen
Telerik team
answered on 22 Nov 2012, 12:25 PM
Hi guys,

Thanks for the idea. We have played around with it and seems to came up with a possible improvement for the described scenario. The idea is to have a  per widget
setting  which  will "defer" the rendering of initialization scripts. Similar to the following:

@(Html.Kendo().Grid<ProductViewModel>()   
    .Name("Grid")   
     .Deferred()
    /*..*/
)
 
@*some other content*@
 
@Html.Kendo().RenderDeferredScripts()

This setting will not be automatically propagated to the child widgets, thus the Grid editing, for example, should still be functional.

What do you think? Will this be suitable for your scenarios?
All the best,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Matt
Top achievements
Rank 1
answered on 22 Nov 2012, 02:49 PM
Thanks Rosen, I'm really pleased you guys have looked into this and it seems I may have been too quick to criticize, my apologies. I think it would certainly address the problems that I experienced. :)
0
Schalk
Top achievements
Rank 1
answered on 24 Nov 2012, 07:05 AM
Hi Rosen,

I was thinking this over and I think I would prefer the following:
  • Initialization scripts stay where they are (just after the elements they initialize), hence not being deferred to the end of the page
  • Deferring should be the default behavior (or some other way)
I was also thinking that due to the way the views get rendered (outer->inner ex, partial view -> view -> layout), that my approach may be a problem because pushing scripts to the stack after calling the proposed @Html.Kendo().RenderDeferredScripts(), would not render those deferred scripts because the views get parsed in a top-down manner.
ex.
@Html.Kendo().RenderDeferredScripts()
<-- Anything after here would not have been pushed to the HttpContext.Items collection prior to the RenderDeferredScripts call -->
@Html.Kendo().Deferred().DropDownListFor(p => p.SomeId).BindTo(Model.SomeItems)
I got thinking and it seems that the obvious and most unobtrusive solution would be to not modify the scripts or the ASP.NET MVC helpers. This would be beneficial to Telerik/Kendo in the sense that it would be platform independent.

The only way to get what I want without requiring a major rewrite of the helpers, would be to actually just stub out the $(document).ready function used to initialize the scripts. I went looking and found Sam Saffron (from StackOverflow) has considered the same approach and according to him it's feasible: Edit: The implementation here seems better

Proposed solution
1. With all scripts rendering just before the closing of the html->body tag:

<!-- Place in head : document.ready stub function -->
<script>(function(w,d,u){w.readyQ=[];w.bindReadyQ=[];function p(x,y){if(x=="ready"){w.bindReadyQ.push(y);}else{w.readyQ.push(x);}};var a={ready:p,bind:p};w.$=w.jQuery=function(f){if(f===d||f===u){return a}else{p(f)}}})(window,document)</script>
<!-- Place after jQuery/Kendo has been referenced -->
<script>(function($,d){$.each(readyQ,function(i,f){$(f)});$.each(bindReadyQ,function(i,f){$(d).bind("ready",f)})})(jQuery,document)</script>

2. With all scripts rendering in the <head> section
No action required

I believe this proposed solution would let us have our cake and eat it too. Another plus for Kendo, it should be portable to any platform that is being catered for.

To account for developer laziness and to make it easier for those starting out with Kendo, I'm also proposing adding two methods to the ASP.NET MVC Helper (and other helpers) to make this approach more robust.

<!-- Place in head : document.ready stub function -->
@Html.Kendo().Defer().PreInit()
<!-- Place after scripts have been referenced (jQuery -> Kendo) -->
@Html.Kendo().Defer().Init()

I'll definitely be using this approach instead of my prior deferral proposition.

Edit: The implementation here seems better

Kind regards,
Schalk

0
Rosen
Telerik team
answered on 26 Nov 2012, 08:28 AM
Hello Schalk,

This deferred scripts approach seems to work ok with partial views. I have attached a small sample which demonstrates this use case. Please take a look, maybe I'm missing something.

Regards,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Schalk
Top achievements
Rank 1
answered on 27 Nov 2012, 08:59 AM
Hi Rosen,

I would still highly recommend that the jQuery document ready helper stub be used instead of the script deferral approach. I have tested the stub method and seems to work fine.

I took a look at the project you attached. Unfortunately I was not able to open the project, but I did browse code via text editor.

Here are some 2 other scenario's to consider with the deferred scripts approach:
  • @Html.Kendo().DeferredScripts() gets called more than once (ex. on View and then later on Layout) with the first @Html.Kendo().DeferredScripts() called after the required resources are available (jQuery/Kendo etc.)
  • Deferred scripts are never output:
    1. Partial View is returned from Action Method with no @Html.Kendo().DeferredScripts() present
    2. Scripts are deferred after calling @Html.Kendo().DeferredScripts() (which means the response gets sent without the deferred scripts included)
On another way to approach this:
The HttpContext.Items collection will persist to Http Handlers.
  1. Assign an arbitrary identifier to the request & add a script reference (ex. ~/deferred.axd?key=) where the deferred scripts should be collected
  2. Collect all the deferred scripts.
  3. On a Http Handler EndRequest add the deferred scripts to the cache using the arbitrary identifier.
  4. The browser will initiate a request to fetch the deferred scripts, whereafter the cache entry can be removed.
It seems like a lot of extra work to just not stub out the jQuery document ready method (which gets replaced with the actual jQuery document ready method once jQuery loads).

Kind regards,
Schalk
0
Rosen
Telerik team
answered on 28 Nov 2012, 12:39 PM
Hello Schalk,

Thanks for the feedback.
If I understood correctly the later approach, you should be able to use it with current version of KendoUI for ASP.NET MVC without any modifications of the code.
However, we think that the DeferredScripts approach is still applicable solution in most of the scenarios. It also does not require inclusion of two more scripts on the page, which also is error prone.

Greetings,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Schalk
Top achievements
Rank 1
answered on 29 Nov 2012, 06:34 AM
Hi Rosen.

You are correct in saying that the document ready stub solution is usable without any modifications of the code.
I thought then that this solution would be the preferred method considering that KendoUI is catering for other platforms - currently ASP.NET MVC and JSP and maybe some others such as Play/Lift,Grails,Django,RoR,PHP etc. later?

I guess the deferred scripts approach could still be useful. Could I then also suggest sharing the pipeline (make it available via html helper Kendo extension) so developers can defer their own scripts?

I honestly can't think of anyone objecting to adding 2 inline scripts so that they can benefit from placing their scripts before the body close of the html page. The only real benefit from deferring script execution is to provide the illusion of improved responsiveness (eg. the page will be usable before all the scripts are executed)

Thanks for the quick responses and good luck on your side.

Kind regards,
Schalk
0
Alexandre Jobin
Top achievements
Rank 1
answered on 14 Dec 2012, 05:14 PM
hi everyone!

just wanted to know what the Kendo team have decided to do to solve the problem of scripts rendered in the middle of the body. In my projects, i declare all my scripts at the end of the body and because Kendo MVC wrapper render his scripts in the middle, i have a "jQuery is not define" error.

For now, the only solution i've found is to put the jQuery and the kendo scripts in the head section but i don't want it to be a permanent solution.

thank you for your help!

0
Rosen
Telerik team
answered on 17 Dec 2012, 08:32 AM
Hi Alexandre,

As I have mentioned, we will implement the DeferredScripts approach for the Q1 2013 version of KendoUI for ASP.NET MVC. Meanwhile, you are free to use all of the approaches described in this thread.


Greetings,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Lorenzo
Top achievements
Rank 1
answered on 25 Jan 2013, 04:10 PM
Dear Rosen,

I have just start to try KendoUI MVC wrapper.
I'm using deferred scripts and it works as expected.

I have some questions about:

  • is it possible to bundle all the controls initialization in one single jQuery initialization function instead of a call for each of the deferred controls?
  • if not, there is plans to implement such thing?
  • Eventually is a contribution accepted?
Lorenzo
0
Rosen
Telerik team
answered on 28 Jan 2013, 12:08 PM
Hi Lorenzo,

I'm afraid that this is currently not possible. However, we may consider implementing such option for future version of the KendoUI for ASP.NET MVC base on the user interest. Therefore, you may consider posting your idea in our user voice.

Regards,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Lorenzo
Top achievements
Rank 1
answered on 28 Jan 2013, 01:13 PM
Is code contributions accepted?

Lorenzo
0
Rosen
Telerik team
answered on 30 Jan 2013, 08:39 AM
Hello Lorenzo,

Feel free to share your code with us here if you like.

Regards,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Vasile
Top achievements
Rank 1
answered on 13 Mar 2013, 04:52 PM
I just used Html.Kendo().DeferredScripts() from beta build and it solved one tricky problem I had using kendo with requirejs.
In the end the solution was to render the script block just inside one of the functions that will be called on require calback function, however I observed that DeferredScripts will output also the <script> tag which may be normal but it is a limitation in my case. I had to use a temporary string to replace all <script> tags with "" to be able to have proper script in my require callback function.

@{
string defferedScript = Html.Kendo().DeferredScripts().ToString();
defferedScript = defferedScript.Replace("<script>","");
defferedScript = defferedScript.Replace("</script>","");
}
<script type="text/javascript">
    require([module], function (m) {
        @Html.Raw(defferedScript);
    });
</script>

Would be excelent to have an option (parameter) in DeferredScripts which will enable us to let Kendo know if we want the <script> tags or not, in my opinion would make sense to have this little option to skip the penalty of discarding the tags with replace.

Thank you
Vasile
0
Rosen
Telerik team
answered on 14 Mar 2013, 08:29 AM
Hello Vasile,

Thanks for the suggestion. We have added such option to DeferredScripts method. It will be available with the Q1 2013 release of KendoUI, which is scheduled for the second half of March.

All the best,
Rosen
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Jacques
Top achievements
Rank 2
answered on 07 Oct 2014, 05:26 AM
The deferred method worked very well for us, allowing us to still add all JavaScript files to the end of the page. 
Tags
General Discussions
Asked by
Matt
Top achievements
Rank 1
Answers by
Atanas Korchev
Telerik team
Matt
Top achievements
Rank 1
LessX
Top achievements
Rank 1
Chris
Top achievements
Rank 1
Schalk
Top achievements
Rank 1
Rosen
Telerik team
Alexandre Jobin
Top achievements
Rank 1
Lorenzo
Top achievements
Rank 1
Vasile
Top achievements
Rank 1
Jacques
Top achievements
Rank 2
Share this question
or