JQuery DatePicker bundled control can't be included in other controls

3.7k views Asked by At

I've been tasked with implementing a Date/Time selector for several areas of our web project, and instructed to use a control that another developer created as part of it. The control I'm working on is supposed to allow the user to choose a date from a calendar, choose a format for the display of that date (from several pre-defined formats, or with a simple text override) and optionally a time string (which is really just freeform text).

The control I was instructed to use is documented here: http://www.west-wind.com/WebLog/posts/213015.aspx, and uses the DatePicker from jQuery.

After I implemented my control and tested it, I began integrating it into the pages which needed Date and/or time inputs. In my testing of those implementations, I discovered a bug: when I include multiple copies of my control on a page, only the first one gets the jQuery calendar. The others are not tied to it.

I have tried some of the methods suggested in a seemingly-related question (titled 'duplicating jquery datepicker'), such as calling the '.datepicker()' function on the west-wind control (which renders a textbox) via the $("css-selector").datepicker() syntax, and ASP.NET is guaranteeing unique IDs for all the text boxes.

So, in summation, it looks like this:

<page>
  <mycontrol>
   <west-windjQuerycontrol />
  </mycontrol>
  <mycontrol>
   <west-windjQuerycontrol />
  </mycontrol>
</page>

Now, the strange part: When there are multiple copies of the west-wind control on the page, without the other user control containing them, they work correctly. Other than the jQuery control, my control has nothing unusual about it: simply labels, textboxes, panels, and dropdowns. Something about bundling the West-Wind jQuery control into a user control seems to be breaking it.

Any advice? I've been banging my head against this for a while, hampered by my poor javascript skills and limited exposure to jQuery.

As pointed out below, it's hard to say without the HTML. I've included it below.

<form name="form1" method="post" action="ControlTest.aspx" id="form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTU4NjEzMDEwOQ9kFgICAw9kFgQCAw9kFgRmD2QWAgIDD2QWAgIDDxBkZBYBZmQCAg9kFgICAw9kFgICAQ8QZGQWAWZkAgUPZBYEZg9kFgICAw9kFgICAw8QZGQWAWZkAgIPZBYCAgMPZBYCAgEPEGRkFgFmZGRDjfLpdb+XxaVaQYP2XkPil2Galw=="     />
</div>

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
            theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
        }
}
//]]>
</script>


        <script src="/SSO/DE/WebResource.axd?d=jMPpL-KK8_mPj_ssZzGblw2&amp;t=633481894229838141" type="text/javascript"></script>


<script src="/SSO/DE/ScriptResource.axd?d=8KwRIGaNAD3hi2Loz3YV-uxgrdZpGe8nnwH5E3gxLW_lQpnYjRbyIYThTnHtD9rt0&amp;t=633613004148118290" type="text/javascript"></script>
<script src="/SSO/DE/ScriptResource.axd?d=8KwRIGaNAD3hi2Loz3YV-uxgrdZpGe8nnwH5E3gxLW-K0Kuw-pGK1O3mE_r1y3sjKmhHtQjSXeMtYSim0bjyGA2&amp;t=633613004148118290" type="text/javascript"></script>
<script src="/SSO/DE/ScriptResource.axd?d=Id5yAacLMZHF7TWlkgrrid30ZStmsXuLHcF6WQ404YLySP4Itj4qxv2wi9ffbsWQA86oLdnZPWkwDnu4NKxfG1Ue7qdGG1SbOfb4ooHVs7M1&amp;t=633481957084709567" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.');
//]]>
</script>

<script src="/SSO/DE/ScriptResource.axd?d=Id5yAacLMZHF7TWlkgrrid30ZStmsXuLHcF6WQ404YLySP4Itj4qxv2wi9ffbsWQhT3MFELBAa2rFJZXnSlYAZIN7RT1npcBxJRsWGjJWIwTF0Es1m0vOd-xYnFqWJKz0&amp;t=633481957084709567" type="text/javascript"></script>
    <div style="margin:25px 10px;width:100%;">
        <script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('stupidThing',     document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
//]]>
</script>

        <div id="datePicker_Div0" class="AdminRowOdd DERow">
            <div id="datePicker_Div1" class="DELabel">
                <span id="datePicker_DateLabel">Date</span>
            </div>
            <div id="datePicker_Div2" class="DEInput datePicker">
                <input name="datePicker$DateSelector" type="text" onchange="javascript:setTimeout('__doPostBack(\'datePicker$DateSelector\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" id="datePicker_DateSelector" style="width:80px;" />
                <select name="datePicker$languageSelector" onchange="javascript:setTimeout('__doPostBack(\'datePicker$languageSelector\',\'\')', 0)" id="datePicker_languageSelector">
    <option selected="selected" value="en-US">en-US</option>
    <option value="fr-CA">fr-CA</option>
    <option value="fr-FR">fr-FR</option>
    <option value="es-ES">es-ES</option>
    <option value="es-MX">es-MX</option>

</select>
            </div>
        </div>
        <div id="datePicker_Div3" class="AdminRowEven DERow">
            <div id="datePicker_Div4" class="DELabel">
                <span id="datePicker_FormatChoiceLabel">Choose your display format:    </span>
            </div>
            <div id="datePicker_Div5" class="DEInput">
                <select name="datePicker$DateFormatSelector" onchange="javascript:setTimeout('__doPostBack(\'datePicker$DateFormatSelector\',\'\')', 0)" id="datePicker_DateFormatSelector">
    <option selected="selected" value="Choose a date first">Choose a date     first</option>

</select>                
            </div>
        </div>
        <div id="datePicker_Div6" class="AdminRowOdd DERow">
            <div id="datePicker_Div7" class="DELabel">
                <span id="datePicker_FormatOverrideLabel">Or enter your own     text</span>
            </div>
            <div id="datePicker_Div8" class="DEInput">
                <input name="datePicker$DateFormatOverride" type="text"     onchange="javascript:setTimeout('__doPostBack(\'datePicker$DateFormatOverride\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;"     id="datePicker_DateFormatOverride" />
            </div>
        </div>

        <br />
        <div id="date1_Div0" class="AdminRowOdd DERow">
            <div id="date1_Div1" class="DELabel">
                <span id="date1_DateLabel">Date</span>
            </div>
            <div id="date1_Div2" class="DEInput datePicker">
            <input name="date1$DateSelector" type="text" onchange="javascript:setTimeout('__doPostBack(\'date1$DateSelector\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" id="date1_DateSelector" style="width:80px;" />
                <select name="date1$languageSelector" onchange="javascript:setTimeout('__doPostBack(\'date1$languageSelector\',\'\')', 0)" id="date1_languageSelector">
    <option selected="selected" value="en-US">en-US</option>
    <option value="fr-CA">fr-CA</option>
    <option value="fr-FR">fr-FR</option>
    <option value="es-ES">es-ES</option>
    <option value="es-MX">es-MX</option>

</select>
            </div>
        </div>
        <div id="date1_Div3" class="AdminRowEven DERow">
            <div id="date1_Div4" class="DELabel">
                <span id="date1_FormatChoiceLabel">Choose your display format:</span>
            </div>
            <div id="date1_Div5" class="DEInput">
                <select name="date1$DateFormatSelector" onchange="javascript:setTimeout('__doPostBack(\'date1$DateFormatSelector\',\'\')', 0)" id="date1_DateFormatSelector">
    <option selected="selected" value="Choose a date first">Choose a date first</option>

</select>                
            </div>
        </div>
        <div id="date1_Div6" class="AdminRowOdd DERow">
            <div id="date1_Div7" class="DELabel">
                <span id="date1_FormatOverrideLabel">Or enter your own text</span>
            </div>
            <div id="date1_Div8" class="DEInput">
                <input name="date1$DateFormatOverride" type="text" onchange="javascript:setTimeout('__doPostBack(\'date1$DateFormatOverride\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" id="date1_DateFormatOverride" />
            </div>
        </div>

    </div>

<div>

    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWFQLr6MeTCwKb1Zr0AwKVt6utCQKIwaTjAQKdwYzzBwLiwsDhDQKIwdCLBAKHwbCtCgLRr42cCQKi9vj4DgK2lM6kBQLLrsUtAsaboRMC2+2u3QgCzu2GzQ4Cse7K3wQC2+3atQ0C1O26kwMCpdTivwwC1o2X2wsCoubqnQk8I1BK30Q/iVw/rExUww2Cs4bicw==" />
</div>

<script type="text/javascript">
//<![CDATA[
jQuery(document).ready( function() {
var cal = jQuery('#datePicker_DateSelector').datepicker({yearRange: '-1500:+100',dateFormat: 'm/d/yy'});
} );
Sys.Application.initialize();
//]]>
</script>
</form>
2

There are 2 answers

2
Shog9 On BEST ANSWER

Hard to know without seeing the generated HTML, but my first guess would be that your control isn't applying class names directly to the <input> field... or you are depending on ids in some way (which would likely change when embedded in a user control).


In the HTML you've posted, these appear to three relevant portions:

Block #1 (control: datePicker)

<div id="datePicker_Div0" class="AdminRowOdd DERow">
    <div id="datePicker_Div1" class="DELabel">
        <span id="datePicker_DateLabel">Date</span>
    </div>
    <div id="datePicker_Div2" class="DEInput datePicker">
        <input name="datePicker$DateSelector" type="text" 
           onchange="javascript:setTimeout('__doPostBack(\'datePicker$DateSelector\',\'\')', 0)" 
           onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" 
           id="datePicker_DateSelector" style="width:80px;" />
        <select name="datePicker$languageSelector" 
           onchange="javascript:setTimeout('__doPostBack(\'datePicker$languageSelector\',\'\')', 0)" 
           id="datePicker_languageSelector">
                <option selected="selected" value="en-US">en-US</option>
                <option value="fr-CA">fr-CA</option>
                <option value="fr-FR">fr-FR</option>
                <option value="es-ES">es-ES</option>
                <option value="es-MX">es-MX</option>
        </select>
    </div>
</div>

...

Block #2 (control: date1)

<div id="date1_Div0" class="AdminRowOdd DERow">
   <div id="date1_Div1" class="DELabel">
       <span id="date1_DateLabel">Date</span>
   </div>
   <div id="date1_Div2" class="DEInput datePicker">
      <input name="date1$DateSelector" type="text" 
         onchange="javascript:setTimeout('__doPostBack(\'date1$DateSelector\',\'\')', 0)" 
         onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" 
         id="date1_DateSelector" style="width:80px;" />
       <select name="date1$languageSelector" 
         onchange="javascript:setTimeout('__doPostBack(\'date1$languageSelector\',\'\')', 0)" 
         id="date1_languageSelector">
         <option selected="selected" value="en-US">en-US</option>
         <option value="fr-CA">fr-CA</option>
         <option value="fr-FR">fr-FR</option>
         <option value="es-ES">es-ES</option>
         <option value="es-MX">es-MX</option>
      </select>
   </div>
</div>

...

Block #3 (jQuery datepicker wireup)

<script type="text/javascript">
   //<![CDATA[
   jQuery(document).ready( function() 
   {
      // matches input element for first control only (id selector)
      var cal = jQuery('#datePicker_DateSelector')
         .datepicker({yearRange: '-1500:+100',dateFormat: 'm/d/yy'});
   });
   Sys.Application.initialize();
   //]]>
</script>

The problem arises from this last block. The selector is specific to the first control, matching by ID. If it was modified to include a separate call for the second control...

      var cal = jQuery('#datePicker_DateSelector')
         .datepicker({yearRange: '-1500:+100',dateFormat: 'm/d/yy'});
      var cal2 = jQuery('#date1_DateSelector')
         .datepicker({yearRange: '-1500:+100',dateFormat: 'm/d/yy'});

...or a more general-purpose selector...

      // matches all text input elements that are descendants of 
      // a div element with a class of datePicker
      var cal = jQuery('div.datePicker input:text')
         .datepicker({yearRange: '-1500:+100',dateFormat: 'm/d/yy'});

...then things should work as expected.

0
Jeff On

I figured out the problem.

The OnPreRender() of the server control builds the javascript found above, the calls the following, where sbStartupScript is a StringBuilder which contains the generated javascript:

sbStartupScript.AppendLine("} );");
scriptProxy.RegisterStartupScript(this.Page, typeof(ControlResources), "_cal" + this.ID,
sbStartupScript.ToString(), true);

The correct form should have been:

sbStartupScript.AppendLine("} );");
scriptProxy.RegisterStartupScript(this.Page, typeof(ControlResources), "_cal" + this.ClientID,
sbStartupScript.ToString(), true);

Now, having the ClientId, the script name is unique, and both will render.