Reputation: 4739
Wow, my first question on StackOverflow!
For the jQuery UI masters that are out there, I'm stuck on this one, which takes a lot. I've read all of the api documentation and keep coming up empty.
So, this example is very simple, and don't worry about why I'm doing this, I'm just looking for an explanation to a simple inconsistency in jQuery UI's .destroy()
method when called on a widget.
In my first example, I call the destroy
method on the datepicker widget, then re-initialize the element as a datepicker widget. This works, as I expected. Since the datepicker does not currently exist, the destroy
method is ignored and the datepicker widget is created.
$("#datepicker").datepicker("destroy").datepicker();
In my second example, I chose another widget, a button this time. I attempt the same exact method as in my first example, except this time, I receive an error, Error: cannot call methods on button prior to initialization; attempted to call method 'destroy'
$("#button").button("destroy").button();
So, how and why is this happening? The documentation reads the exact same way for both widgets datepicker; button. I find it odd that it would only be implemented this way for a datepicker widget and not for all of the other native jQuery UI widgets.
Just in case anyone is curious, I'm able to reproduce the same results in Chrome 47.0.2526.106 m & IE 11.0.9600.18097 (update 11.0.25)
Also, to make it nice an easy on you, here is a working code snippet that shows both examples, the first one is working, and the second one throws the error shown below the element. Just to explore all options, I went ahead and added a third example to the snippet with a dialog widget, and receive the same error.
(function ($){
try {
$("#datepicker").datepicker("destroy").datepicker();
}
catch (e) {
$("#datepickerError").html(e);
}
try {
$("#button").button("destroy").button();
}
catch (e) {
$("#buttonError").html(e);
}
try {
$("#dialog").dialog("destroy").dialog();
}
catch (e) {
$("#dialogError").html(e);
}
})(window.jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<input type="text" id="datepicker" />
<br />
<span id="datepickerError">(no error)</span>
<br /><br />
<input type="button" id="button" value="Button" />
<br />
<span id="buttonError">(no error)</span>
<br />
<br />
<div id="dialog" title="test"></div>
<br />
<span id="dialogError">(no error)</span>
Upvotes: 4
Views: 1337
Reputation: 9691
I believe this is because DatePicker isn't a real jquery widget in the sense that it doesn't use the widget factory like the other ones do. It uses the $.fn
prototype to define itself. From the code it does this to create that date picker method
$.fn.datepicker = function(options){
while widget factory widgets register themselves with the widget factory and the factory handles creating methods:
$.widget( "ui.button", { .... });
In fact I believe its still in their path to rewrite it using widget factory.
http://wiki.jqueryui.com/w/page/12137778/Datepicker
The specific code and functionality changes we want to address in this refactor are:
Refactor the code to use the widget factory and follow the jQuery UI API
The widget factory is the thing that handles all the widget magic, one of which is finding the elements instance that widget has been called on and determining if its been instantiated or not and displaying that error. But since DatePicker doesn't use widget factory it probably doesn't fail with that widget factory error and most likely gracefully fails as most jquery stuff does when there is no elements in the jquery object you are operating on.
In fact if you unselect everything in the jquery ui download and only select DatePicker and whatever dependencies it says it needs. Look at the downloaded file. Widget factory isn't even included and either is the error message. Now check off widget under UI Core and download it again. Search for that error message again. There it is! Under the $.widget.bridge
function.
if ( !instance ) {
return $.error( "cannot call methods on " + name + " prior to initialization; " +
"attempted to call method '" + options + "'" );
}
And if we look at the date picker code you can see when you pass it options it tries to build method names under certain circumstances. One of which is _destroyDatepicker
. There is no code directly in the $.fn
extension function that stops it from calling anything when elements have not been instantiated with the datePicker so it builds that function name and calls the function and one of the first few lines in the destroy is
if (!$target.hasClass(this.markerClassName)) {
return;
}
So this will most likely be where it bails out without throwing any kind of error.
In short, you are right. It is inconsistent. There is a reason why but unless you are familiar with their code it is not obvious at all.
Upvotes: 1