MVC 3, reuse of partial views and jquery, without conflicting the DOM

喜夏-厌秋 提交于 2019-12-03 11:36:55
Mrchief

Yes. As you pointed out, do not use ids and id selectors in your JavaScript. Instead use class selectors:

E.g., in your view's markup:

<div class="container">Partial View content</div>

JS:

var $div = $('div.container');
// do something

To eliminate possibility of selecting other tags with same class name, assign a programmatic name the elements in partial view which is used only as a selector handle and not as a CSS class.

While ID based lookup is the best performance wise, in this case, it makes more sense to go by the [tag+class] based lookup to avoid id conflicts. [tag+class] based lookup comes pretty close to id selectors in terms of performance.

Also, you can gain further improvement by limiting the lookup scope:

<div class="container">Partial View content <span class="child">Child content </span></div>

var $span = $(span.child')  // scope of lookup here is entire document

However, if you know that child is inside container div, you can limit the scope by saying:

var $div = $('div.container').children('span.child'); // or just '.child'

Another tip is to do the lookup once and reuse it:

// less performant
function doSomething() {

    // do something here
    $('div.container').css('color', 'red');

    // do other things
    $('div.container').find('.child');

   // do more things
    $('div.container').click(function() {...});
}


// better
function doSomething() {
    var $div = $('div.container');

    // do something here
    $div.css('color', 'red');

    // do other things
    $div.find('.child');

   // do more things
    $div.click(function() {...});

   // or chaining them when appropriate
   $('div.container').css('color', 'red').click(function() { ... });


}

Update: Refactoring OP's post to demo the concept:

<script type="text/javascript">

       function SetProductTabContent(selectedTab, ctx) {
            var $container = $("div.pv_productDescriptionContent", ctx);

            // this will find only the immediate child (as you had shown with '>' selector)
            $container.children('div').css('display', 'none');  

            switch (selectedTab) {

                case '#tab-1':
                    $('div.pv_productDescriptionText', $container).css('display', 'block');
                    // or $container.children('div.pv_productDescriptionText').css('display', 'block');
                    break;

                case '#tab-2':
                    $('div.pv_productSpecificationText', $container).css('display', 'block');
                    // or $container.children('div.pv_productSpecificationText').css('display', 'block');
                    break;   
            }


    function SetUpMenuItems(ctx) {
        // Get all the menu items within the passed in context (parent element)
        var menuItems = $("div.pv_productMenu a", ctx);

        // Select the first tab as default
        menuItems.first().addClass("menuItemActive");

        // Handle the look of the tabs, when user selects one. 
        menuItems.click(function () {

            var item = $(this);

            // Get content for the selected tab
            SetProductTabContent(item.attr('href'), ctx);

            menuItems.removeClass("menuItemActive");
            item.addClass("menuItemActive");
            return false;
        });
    }
    </script>


<div style="" class="pv_productMenu">
    <a href="#tab-1">
        <div class="menuItemHeader">
            Menu1</div>
    </a><a href="#tab-2">
        <div class="menuItemHeader">
            Menu2
        </div>
    </a>
</div>
<div class="pv_productDescriptionContent">
    <div class="pv_productDescriptionText" style="display: none;">
        <%: Model.Product.Description %>
    </div>
    <div class="pv_productSpecificationText" style="display: none;">
        <%: Model.Product.Description2%>
    </div>
</div>

Note: I removed document.ready wrapper since that will not fire when you load the partial view. Instead, I refactored your View's JS to call the setup function and also pass in the scope (which will avoid selecting other divs with same class):

// Fetch content in the background
$.get(url, input, function (result, response) {
       $('#dialogBox').html(result);
       SetUpMenuItems($('#dialogBox'));   
});

Obviously, you can modify this further as you deem fit in your app, what I've shown is an idea and not the final solution.

  • If you load #dialog again, they will overwrite existing markup, hence there won't be duplicate.
  • If you load the partial view again in some other container, you can pass that as the context and that will prevent you accessing the children of #dialog
  • I came up with this arbitrary prefix pv_ for programmatic class handles. That way, you can tell looking at the class name if it is for CSS or for use in your script.

Simplest way to do that is, make ids of products as part of html tags ids

something like that

<input type="text" id="txt_<%=Model.ID%>"> 

if you use Razor

<input type="text" id="txt_@Model.ID"> 

I'm surprised this hasn't come up more often. I guess most developers aren't creating their own controls with partials. Here is something that I came with that is works pretty well and not that hard to implement in MVC4.

First I tried passing a dynamic model to the partial and that doesn't work. (sad face) Then I went the typed partial view route. I created a collection of objects called steps (because build a wizard control).

public class WizardStep
{
    public string Id { get; set; }
    public string Class { get; set; }
}

public class WizardSteps
{

    public WizardSteps()
    {
        Steps = new List<WizardStep>();
        Steps.Add(new WizardStep() {Id = "Step1"});
        Steps.Add(new WizardStep() { Id = "Step2" });
        Steps.Add(new WizardStep() { Id = "Step3" });
        Steps.Add(new WizardStep() { Id = "Step4" });
        Steps.Add(new WizardStep() { Id = "Step5" });

    }
    public List<WizardStep> Steps { get; set; }

}

Razor code looks like this:

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.First())

or

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.Skip(1).First() )

or

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.Skip(2).First())

and more

@Html.Partial("_WizardButtonPanel",@Model.WizardSteps.Steps.Skip(3).First())

The partial view looks something like this:

@model SomeProject.Models.WizardStep
<div id="buttonpanel-@Model.Id" >
<a id="link-@Model.Id" >Somelinke</a>
</div>

Happy coding...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!