The following are all proposed features for jQuery 1.2 (it's very likely that many of the features here won't make the final cut - but this is what's currently desired to arrive in 1.2).
Contents |
Supporting the XPath-like [@attr] syntax is rather silly at this point, considering the number of other JavaScript libraries that support CSS selectors. We should be supporting the standard syntax for this selection.
However, we could get away with this, without much pain, by simply ignoring the @ character if it exists (allowing for some degree of backwards compatibility).
An implementation would allow for results that look like:
$("img[src]")
$("a[href^=google]")
Currently, we support the XPath-like [selector] syntax. This should be removed in favor of better CSS selector support ([attr], [attr=value], etc.). Instead, this functionality (while still useful) should be delegated to a new :has(selector) selector.
An implementation would work something like this (finding all the divs that have a paragraph inside of them):
$("div:has(p)")
Here's a possible implementation:
jQuery.expr[":"].has = "jQuery.find(m[3],a).length";
Our current means of form/field serialization are very poor. We currently provide two methods:
These two methods are lacking for real-world use. Two methods from the Forms plugin should be considered for import:
There are a number of open bugs that relate to .val() and .serialize() that would be solved with the introduction of these new methods.
An important alternative to .wrap() is the ability to wrap a single element around all elements matched by a selector. This could be achieved with a wrapAll method, like the following:
$.fn.wrapAll = function() {
// There needs to be at least one matched element for this to work
if ( !this.length ) return this;
// Find the element that we're wrapping with
var b = jQuery.clean(arguments)[0];
// Make sure that its in the right position in the DOM
this[0].parentNode.insertBefore( b, this[0] );
// Find its lowest point
while ( b.firstChild ) b = b.firstChild;
// And add all the elements there
return this.appendTo(b);
};
Additionally, the existing .wrap() method could be made to use .wrapAll() (allowing us to save some bytes in its addition).
A demo can be found here.
Currently jQuery has .next() and .prev(), which are limited to only retrieving immediately-adjacent elements. Being able to retrieve all previous siblings and all next siblings (and being able to filter them) is highly desirable.
Here's a sample implementaton of .nextUntil():
$.fn.nextUntil = function(expr) {
var match = [];
// We need to figure out which elements to push onto the array
this.each(function(){
// Traverse through the sibling nodes
for( var i = this.nextSibling; i; i = i.nextSibling ) {
// Make sure that we're only dealing with elements
if ( i.nodeType != 1 ) continue;
// If we find a match then we need to stop
if ( jQuery.filter( expr, [i] ).r.length ) break;
// Otherwise, add it on to the stack
match.push( i );
}
});
return this.pushStack( match, arguments );
};
A demo of this in action can be found here.
It's important to note the dual nature of this method:
Find all elements up until the next h3:
$("h3").nextUntil("h3");
=> [ div, p, p ]
Find all elements after this one:
$("h3").nextUntil();
=> [ div, p, p, h3, div, p ]
Currently, all the methods that allow users to select elements along a specific axis (parents, find, children, next, prev, siblings) don't allow you to include the starting element itself.
It's most obvious with siblings:
$("p").siblings();
=> [ div, div, div ]
With .andSelf() you would get:
$("p").siblings().andSelf();
=> [ div, div, p, div ]
Combining this functionality with .wrapAll() and .nextUntil you could wrap a collection of related elements in a single line of code:
$("h3#head").nextUntil("h3").andSelf().wrapAll("<div></div>");
An implementation of andSelf would look like:
$.fn.andSelf = function(){
return this.add( this.prevObject );
};
There has been some strong demand for the ability to access the full contents (including text nodes) within an element. There are two specific use cases that this would solve:
$("#foo").contents().appendTo("#bar");
$("a").contents().wrap("<b></b>");
It's important to note that this would be the first jQuery method capable of returning non-element nodes. Thusly, most jQuery methods should be extra-sure not to throw exceptions when dealing with incorrect nodes. (Additionally, the documentation for this method should be explicit in seeding concern in this matter.)
One possible solution:
jQuery.fn.contents = function() {
return this.pushStack( jQuery.map( this, "jQuery.makeArray(a.childNodes)" ) );
};
Additionally, this may be a good time to consider extending .contents() to handle the contents of obtuse elements. For example:
It could return the contentDocument of the specified iframe:
$("iframe").contents().find("body").append("hello!");
=> [ body ]
It could return all form elements in a form:
$("form").contents();
=> [ input, input, select, input ]
It could return all options in a select:
$("select").contents();
=> [ option, option ]
I think the iframe use case is especially important, and one that we may want to consider.
Please see the related bug for more information.
This would be a simple alias for .is(".className"). It's an oft-requested feature, and the current API doesn't make it obvious that this is what needs to occur.
A possible implementation:
jQuery.fn.hasClass = function(c){
return this.is("." + c);
};
The second most popular use case of loading HTML into a document (via an Ajax call) is to load the HTML and append it to another element. (Of course, prepend may also be rather popular in this instance.) We should seriously consider a way to make .load() viable for other types of insertion.
The most obvious solution is to adapt .load() to handle multiple types of insertion, such as appending the returned HTML instead of using .html().
In order to achieve this, two things would need to occur:
The new .load() signature would look something like the following:
.load( url, params, insertion, callback )
Allowing you to do:
$("#foo").load("test.html", "append");
This argument would be completely optional (having its default value be equivalent to 'html').
Currently .getScript() uses $.ajax() to request scripts. However, this use case is highly limited. The ability to load remote scripts would be highly useful.
Two things are required in order to make this happen:
The second one is especially critical, since getScript() provides the user with a callback, to let them know once the script has loaded. There are easy solutions for all browsers but Safari. Some of the most recent techniques revolve around the synchronous nature of script loading - but a reusable implementation needs to be well defined.
The final solution would allow you to do something like this:
$.getScript("http://foo.com/bar.js");
This addition would allow users to dynamically load remote scripts and have them be executed before the main flow of the application commences.
This is very useful functionality, and similar to the features provided by Dojo, Mochikit, and OpenJSAN.
A solution would allow users to do the following:
$.getScript("http://foo.com/bar.js");
$.getScript("/my/test.js");
$(document).ready(function(){
bar(); // use bar from bar.js
test(); // use test from test.js
});
This proposal currently doesn't call for the scripts in getScript to be loaded synchronously - meaning that the above test.js could be loaded, and executed, before bar.js.
The use case of binding a single function to multiple, different, events is a common one - especially when dealing with mouse, or keyboard, events. This addition would allow users to perform that action with a single call to .bind(), behaving similarly to .addClass() and .removeClass().
The final call would allow you to do:
.bind("mouseover focus", myfn);
The space-separated style would mimic that of addClass/removeClass (helping to keep us consistent with the rest of the API).
Please see the related bug and patch for more information.
Currently you can clone a set of elements, but when that occurs, their associated event handlers are lost. It would be good to provide a way to clone elements while keeping their handlers intact.
Brandon has already done considerable work on this topic - so importing his solution would probably be desired.
The final result allows you to do something like:
$("#foo").cloneWithEvents().appendTo("#bar");
However, having an extra method for this functionality seems redundant. Currently the only argument to .clone() is a boolean (representing if the clone should be "deep" or "shallow"). A shallow clone is rarely, if ever, used. (And can be easily duplicated by doing: .clone().empty().) I propose that we change the boolean argument of .clone() to mean "clone elements and events" (with the default being "don't clone events").
The result would then become:
$("div").click(myFn);
$("div:last").clone(true).appendTo("body");
$("div:last").click(); // triggers myFn
Triggering events was changed in jQuery 1.1 to trigger the default browser event after calling handlers. This causes as many issues as it solves, #873 proposes a good way to enhance this.
It should be considered to swap trigger and triggerAction, with trigger being the default for click() and alike. An API change seems to be necessary anyway.
A critical aspect of impressive animations is the ability to stop an animation mid-stream. Support for this will be broken down into a couple factors:
Combined, the final result would look something like this:
$("div[@animated]").stop().animate({ top: 0, left: 0 }, "slow");
Currently, all elements being animated are auto-queued based upon the element that they're animating against. While this is useful for most use cases, it is rather unacceptable for most serious forms of animation.
Support for this feature will be broken down into a couple features:
An implementation would work like so:
$("#foo")
.stop() // Stop any running animations
.queue([]) // Empty the queue
.animate({ top: 100, left: 100 }, "fast")
// For the second animation to skip the queue
.animate({ width: 200, height: 200 }, { duration: "slow", queue: false });
Currently, these are the only two DOM properties that users want to animate - but making it such that any generic DOM property could be animated using .animate(), in favor of a CSS property, would generally be a good idea.
An example implementation can be found here. With the user's final code looking something like this:
$("input").click(function(){
$("body")
.animate({ scrollTop: 200 }, "slow")
.animate({ scrollTop: 0 }, 2000, "bounceout");
});
Animating an element, relatively, from a current position is, currently, a major hassle. There's some code in place that could make it easier, with just a little bit of work. This is a proposal for adding a new set of values as arguments to .animate().
It is best illustrated with an example:
$("#foo").animate({
top: 100, // Animate from the current top to 100 pixels
left: "+100", // Animate from the current left to "left + 100 pixels"
height: "-50", // Animate the height to "height - 50 pixels"
width: "hide" // Animate the width to 0 pixels, then set its display to none
});
The relative animations would always be signified by a string beginning with either a "+" or a "-" followed by an integer. This would be parsed and a correct animation value would be determined from it.
When the combination of "{queue: false}" and relative animation is used, a different style of animation should take place. Instead of animating from A to B, the animation should animate (relatively) from 0 to (B - A), adding incremental amounts of pixels every time it's called. Thus if you were to do:
$("#foo")
.css( "top", 100 )
.animate({ top: "+100" }, { queue: false })
.animate({ top: "+100" }, { queue: false });
The final result would be an animation going from 100px to 300px.
The calculation would work something like this (for each animation):
max_val = 100 // User input
duration = 600 // User input
last_val = 0
start_time = current_time
For every step
prct = (current_time - start_time) / duration
if ( prct >= 1 )
cur_val = max_val
else
cur_val = max_val * prct
val = val + (cur_val - last_val)
last_val = cur_val
Currently running the .height() or .width() methods against document or window return unusable results. I propose that we import the functionality from the Dimensions plugin.
The code required to do so would look something like this:
jQuery.each( [ "Height", "Width" ], function(i,name){
var n = name.toLowerCase();
jQuery.fn[ n ] = function(h) {
if ( this[0] == window )
return self["inner" + name] ||
$.boxModel && document.documentElement["client" + name] ||
document.body["client" + name];
if ( this[0] == document )
return Math.max(
document.body["scroll" + name],
document.body["offset" + name]
);
return h == undefined ?
( this.length ? jQuery.css( this[0], n ) : null ) :
this.css( n, h.constructor == String ? h : h + "px" );
};
});
Wrapping jQuery in a closure will have some very interesting results and things that will need to be considered:
In short, it will make jQuery smaller, more compressible, more reusable, and more malleable; all around a great option for inclusion.
Currently, jQuery documentation is included inline, in the form of ScriptDoc comments. In order to be more maintainable, the documentation should be moved to an external location and managed from there.
The following are pieces of functionality that may be removed in jQuery 1.2.
These are other items that were being considered, but have since been re-thought.
Using XPath to select elements in an HTML document is the most obvious way to improve the speed of certain jQuery selectors in Firefox, Opera, and Webkit.
There are two schools of thought on this. Prototype rewrites all CSS selectors to XPath in Firefox, et. al., whereas Dojo selectively re-writes some queries.
Example code from Prototype:
document._getElementsByXPath = function(expression, parentElement) {
var results = [];
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i++)
results.push(query.snapshotItem(i));
return results;
};
Personally, I feel that Dojo's solution is more inline with how we should tackle the problem. Implementing a full CSS Selector -> XPath Selector routine is byte costly. Whereas, we get the most benefit from three types of queries:
These less-popular queries would also benefit, if we were to consider them:
Detecting those three query types and selectively using XPath would seem to be the right solution for us.
Although, if we were to implement that much CSS -> XPath conversion, it may simply result that a full conversion would be ideal, but that remains to be seen.
Based upon the plugin written by Brandon - providing native support for behaviors.
However, before this becomes a serious option for inclusion in 1.2 I would like to see more wide-scale adoption of its use. If that were to occur, I would be more inclined to vouch for its inclusion (even though it is very, very, cool).
In short, Live Query will allow you to do two things very easily:
The result of which would look something like this:
$("ul > li").livequery("click", myFn);
making it such that anytime a new li is inserted into the document (that matches "ul > li"), it will have the myFn function bound to its click event.
or:
$("div").livequery(function(){
$(this).addClass("test");
});
and then any time a new div is inserted into the document, a class will be added to it.
This has a compelling use case, but its file size is daunting. Further clarification is needed here.
This was shelved after a failed implementation in 1.1, I'm still not convinced that this is particularly useful (or could be made useful in a reasonable number of bytes).