最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Including JavaScript at bottom of page, from Partial Views - Stack Overflow

programmeradmin2浏览0评论

Let's say I have a javascript slide-show in a partial view...

_Slideshow.cshtml:

@{
    ViewBag.Title = "Slide Show";
}
<div id="slides">
</div>
<script src="js/slides.min.jquery.js"></script>
<script type="text/javascript">
$(function(){
    $('#slides').slides({
        // slide show configuration...
    });
});
</script>

But I want to be a good little web developer, and make sure all of my scripts go at the bottom of the page. So I'll make my *_Layout.cshtml* page look like this:

_Layout.cshtml:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/css/global.css")" rel="stylesheet" type="text/css" />
</head>
<body>
    <div id="wrapper">
    @RenderBody
    </div>
    <!-- Being a good little web developer, I include my scripts at the BOTTOM, yay!
    -->
    <script src=".min.js" type="text/javascript"></script>
</body>
</html>

But UH OH! What do I do now, because my slide show script ends up above my jQuery inclusion?! It wouldn't be a big deal if I wanted my slide show on every page, but I only want the slide show partial view to be rendered on a certain page, and.... I WANT MY SCRIPTS AT THE BOTTOM! What to do?

Let's say I have a javascript slide-show in a partial view...

_Slideshow.cshtml:

@{
    ViewBag.Title = "Slide Show";
}
<div id="slides">
</div>
<script src="js/slides.min.jquery.js"></script>
<script type="text/javascript">
$(function(){
    $('#slides').slides({
        // slide show configuration...
    });
});
</script>

But I want to be a good little web developer, and make sure all of my scripts go at the bottom of the page. So I'll make my *_Layout.cshtml* page look like this:

_Layout.cshtml:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/css/global.css")" rel="stylesheet" type="text/css" />
</head>
<body>
    <div id="wrapper">
    @RenderBody
    </div>
    <!-- Being a good little web developer, I include my scripts at the BOTTOM, yay!
    -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
</body>
</html>

But UH OH! What do I do now, because my slide show script ends up above my jQuery inclusion?! It wouldn't be a big deal if I wanted my slide show on every page, but I only want the slide show partial view to be rendered on a certain page, and.... I WANT MY SCRIPTS AT THE BOTTOM! What to do?

Share Improve this question edited Nov 17, 2011 at 20:37 Didaxis asked Nov 17, 2011 at 20:31 DidaxisDidaxis 8,7467 gold badges54 silver badges89 bronze badges 2
  • If you don't need that script on every page, why include it in _Layout? – Maess Commented Nov 17, 2011 at 20:37
  • I want the jQuery library on every page, and therefore in _Layout.cshtml. I DON'T want the slide show on every page... What I'm concerned about, is how the HTML is rendered. I want to be able to have the slide show scripts at the bottom of the (rendered) page – Didaxis Commented Nov 17, 2011 at 20:39
Add a comment  | 

2 Answers 2

Reset to default 11

You could define a section in your layout page for the scripts like this:

<body>
    <div id="wrapper">
    @RenderBody
    </div>
    <!-- Being a good little web developer, I include my scripts at the BOTTOM, yay!
    -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
    @RenderSection("myScripts")
</body>

Then on your pages you define what goes in that section:

@{
    ViewBag.Title = "Slide Show";
}
<div id="slides">
</div>
@section myScripts { //put your scripts here
    <script src="js/slides.min.jquery.js"></script>
    <script type="text/javascript">
    $(function(){
        $('#slides').slides({
            // slide show configuration...
        });
    });
    </script>
}

Then when the page renders, it will take everything in your section and add it to where it is supposed to go on your layout page (in this case, at the bottom).

Note: The accepted solution won't work for partial views as the question asks for.

The Problem

In the normal flow, you can define the contents for a particular section from inside of the parent view on your ActionResult using a @section SectionName {} declaration. And when that view is finally inserted into its LayoutPage, it can call RenderSection to place those contents anywhere within the page, allowing you to define some inline JavaScript that can be rendered and parsed at the bottom of the page after any core libraries that it depends on like this:

The problem arises when you want to be able to reuse the full page view inside of a partial view. Perhaps you'd like to also re-use the view as a widget or dialog from inside of another page. In which case, the full Partial View is rendered in its entirety wherever you've placed the call to @Html.EditorFor or @Html.Partial inside of the Parent View like this:

According to the MSDN Docs on Layouts with Razor Syntax:

  • Sections defined in a view are available only in its immediate layout page.
  • Sections cannot be referenced from partials, view components, or other parts of the view system.
  • The body and all sections in a content page must all be rendered by the layout page

In that scenario, it becomes tricky to get the script defined into the partial view to the bottom of the page. Per the docs, you can only call RenderSection from the layout view and you cannot define the @section contents from inside of a partial view, so everything gets lumped into the same area and your script will be rendered, parsed, and run from the middle of your HTML page, instead of at the bottom, after any libraries it might depend on.

The Solution

For a full discussion of the many ways to inject sections from partial views into your page, I'd start with the following two questions on StackOverflow:

  • Injecting content into specific sections from a partial view with Razor View Engine
  • Using sections in Editor/Display templates

The varying solutions therein differ on support for nesting, ordering, multiple script support, different content types, calling syntax, and reusability. But however you slice it, pretty much any solution will have to accomplish two basic tasks:

  1. Gradually build script objects onto your request from within any page, partial view, or template, probably leveraging some kind of HtmlHelper extension for reusability.
  2. Render that script object onto your layout page. Since the layout page actually renders last, this is simply emitting the object we've been building onto the master page.

Here's a simple implementation by Darin Dimitrov

Add the Helper Extension Methods which will allow you to build arbitrary script objects into the ViewContent.HttpContext.Items collection and subsequently fetch and render them later.

Utilities.cs

public static class HtmlExtensions
{
    public static MvcHtmlString Script(this HtmlHelper htmlHelper, Func<object, HelperResult> template)
    {
        htmlHelper.ViewContext.HttpContext.Items["_script_" + Guid.NewGuid()] = template;
        return MvcHtmlString.Empty;
    }

    public static IHtmlString RenderScripts(this HtmlHelper htmlHelper)
    {
        foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys)
        {
            if (key.ToString().StartsWith("_script_"))
            {
                var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func<object, HelperResult>;
                if (template != null)
                {
                    htmlHelper.ViewContext.Writer.Write(template(null));
                }
            }
        }
        return MvcHtmlString.Empty;
    }
}

Then you can use like this within your application

Build this script objects like this inside of your Partial View like this:

@Html.Script(
    @<script>
         $(function() {
             $("#@Html.IdFor(model => model.FirstName)").change(function() {
                 alert("New value is '" + this.value + "'");
             });
         })
     </script>
)

And then render them anywhere within your LayoutPage like this:

@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
@Html.RenderScripts()
发布评论

评论列表(0)

  1. 暂无评论