Skip to main content

Bug Tracker

Side navigation

#13498 closed feature (wontfix)

Opened February 21, 2013 09:51PM UTC

Closed February 21, 2013 10:05PM UTC

Last modified February 22, 2013 07:54PM UTC

New Traversing Methods; .nextFirst() .prevFirst()

Reported by: Panman8201 Owned by:
Priority: undecided Milestone: None
Component: unfiled Version: git
Keywords: Cc:
Blocked by: Blocking:
Description

I'd like to propose a couple new traversing methods to provide a solution to common misconceptions.

When using the .next() and .prev() traversing methods with a [Selector] I thought they would give me the next/previous sibling that matched the selector. No, the give the directly adjacent sibling if it matches the selector.

After doing some Googling, I found that this is somewhat of a common misconception (for those who didn't read the documentation clearly enough). The workaround seemed strait forward, use .prevAll([Selector]:first).

Ok, that works for one matched element in the calling set. However, if there are multiple elements to apply the .prevAll() to then it only returns the :first matched element of the entire list. Basically, the :first part of the [Selector] is applied at the end, against the entire list of next/previous siblings.

What I would like to see are "jQuery Official" traversing methods to get the next/previous sibling that match a Selector. The following is a plug-in that mimics this feature request but I feel that it could be done better in the jQuery core (which I did dig into but found myself clueless in the traversing.js file).

(function( $ ) {
	$.fn.nextFirst = function( type ) {
		return this.map(function() {
			return $(this).nextAll( type + ":first" );
		});
	};
	$.fn.prevFirst = function( type ) {
		return this.map(function() {
			return $(this).prevAll( type + ":first" );
		});
	}
})(jQuery);

Here is some test code to provide an example how this works:

<!DOCTYPE html>
<html lang="en">
<head>
	<title>jQuery .prevFirst() Example</title>
	<meta charset="utf-8">
	<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
	<script>
	(function( $ ) {
		$.fn.nextFirst = function( type ) {
			return this.map(function() {
				return $(this).nextAll( type + ":first" );
			});
		};
		$.fn.prevFirst = function( type ) {
			return this.map(function() {
				return $(this).prevAll( type + ":first" );
			});
		}
	})(jQuery);

	$(function(){
		var	prevResult = $(".mydivider").prev(".divider"),
			prevAllResult = $(".mydivider").prevAll(".divider:first"),
			prevFirstResult = $(".mydivider").prevFirst(".divider");
		$("#prev-result").html(prevResult.text());
		$("#prevAll-result").html(prevAllResult.text());
		$("#prevFirst-result").html(prevFirstResult.text());
	});
	</script>
</head>
<body>
<h1>Setup List</h1>
<ul>
	<li class="divider">Divider A </li>
	<li>Sub Text</li>
	<li class="mydivider">Sub Text</li>
	<li>Sub Text</li>
	<li class="divider">Divider B </li>
	<li>Sub Text</li>
	<li>Sub Text</li>
	<li>Sub Text</li>
	<li class="divider">Divider C </li>
	<li class="mydivider">Sub Text</li>
	<li>Sub Text</li>
	<li>Sub Text</li>
</ul>
<h1>Expected Result</h1>
<p>Divider A Divider C </p>
<h1>Actual Results</h1>
<p><strong><code>.prev()</code> Misconception</strong> - <span id="prev-result"></span></p>
<p><strong><code>.prevAll()</code> Workaround</strong> - <span id="prevAll-result"></span></p>
<p><strong><code>.prevFirst()</code> Solution</strong> - <span id="prevFirst-result"></span></p>
</body>
</html>

At the very least, I would expect this to be a bug of the .prevAll() workaround. Thoughts?

Attachments (0)
Change History (9)

Changed February 21, 2013 09:58PM UTC by Panman8201 comment:1

Ok, apparently I'm not very good at fully reading the directions... Here is the test case on jsFiddle: http://jsfiddle.net/jc4sV/

P.S. I tried modifying the ticket to remove my pasted example but could not find an option to do so.

Changed February 21, 2013 10:05PM UTC by rwaldron comment:2

resolution: → wontfix
status: newclosed

These would make a great plugin: http://plugins.jquery.com/

Changed February 22, 2013 02:40PM UTC by Panman8201 comment:3

Well, that was a swift "No". Care to give a more technical reason? Maybe a second, honest opinion? I just feel that it's a better fit for core as other plugin creators can rely on the feature being there, and it would be far more useful to end users if in the core. Any chance it would have a better chance if I manage to find a way to add it to traversing.js? Thanks

Changed February 22, 2013 02:48PM UTC by scottgonzalez comment:4

Care to give a more technical reason?

It's uncommon, possible, and easily abstracted into a plugin. Accepting all little features like this would bloat the core to twice its size (just look through the history of tickets if you want).

Maybe a second, honest opinion?

Honestly, the naming is confusing and this doesn't belong in core.

I just feel that it's a better fit for core as other plugin creators can rely on the feature being there

Not too many plugins need this. Those that do, can already accomplish it very easily.

...it would be far more useful to end users if in the core.

It's exactly as useful to end users in the core and in a plugin. Where code is defined is orthogonal to usefulness.

Any chance it would have a better chance if I manage to find a way to add it to traversing.js?

No.

Changed February 22, 2013 03:09PM UTC by ajpiano comment:5

Also, using .first() instead of the :first pseudoselector would be wildly advantageous in an implementation. These types of "I think this is a useful traversal shortcut" requests come up often, and generally they are better left in the applications that do in fact require them, rather than us providing saccharin in core for every traversal pattern that anyone has needed to write their own $.fn method for.

Changed February 22, 2013 03:38PM UTC by Panman8201 comment:6

Replying to [comment:4 scott.gonzalez]:

It's uncommon, possible, and easily abstracted into a plugin. Accepting all little features like this would bloat the core to twice its size (just look through the history of tickets if you want).

Fair enough.

Honestly, the naming is confusing and this doesn't belong in core.

I agree, the name isn't perfect. Just tried to come up with another name that mimics how I would think .prev([Selector]) should naturally lead to.

Not too many plugins need this. Those that do, can already accomplish it very easily.

True, if I need this I'll add it to my own code base. However, I was working on a Pull Request for jQuery Mobile where this would have been helpful.

It's exactly as useful to end users in the core and in a plugin. Where code is defined is orthogonal to usefulness.

Maybe usefulness was the wrong word, "Used" would probably be better. Hardly anyone is going to actively search for such a plugin, but if it was built-in obviously they'd use it.

One last question if you don't mind, do you feel that they way .prevAll() applies the [Selector] (at the end) is correct? To me it should be applied to each matched element in the calling "result set". I can start another ticket if needed. Thanks again!

Changed February 22, 2013 03:49PM UTC by Panman8201 comment:7

Replying to [comment:5 ajpiano]:

Also, using .first() instead of the :first pseudoselector would be wildly advantageous in an implementation.

Thanks, I have updated my plugin to use .first(). However, I know that won't work in the .prevAll() workaround.

These types of "I think this is a useful traversal shortcut" requests come up often,

I wasn't aware that other traversing options were a common request. In my opinion, I think it might be a good idea to take a look at some of the most requested and possible performance improvements if included into core. Again, just my opinion. I can understand the difficulties managing an open source project..

Changed February 22, 2013 04:52PM UTC by dmethvin comment:8

When using the .next() and .prev() traversing methods with a [Selector] I thought they would give me the next/previous sibling that matched the selector.

Even though the documentation says this?

Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector -- http://api.jquery.com/next/
The workaround seemed strait forward, use .prevAll([Selector]:first).

Yes, or add a class name to the markup for example. That might be a better solution depending on the situation. If I was reading code I'd certainly prefer that to a custom traversal method.

The whole reason we created a dead-simple way to extend jQuery was so that people could adapt it to their own needs. Our API is already difficult to learn completely because of its size. Kinda-like-that-but-slightly-different methods just make it more difficult. If anything we've been trying to pare down the rarely used things in the last few versions in favor of them being plugins.

Plugins let you create a dialect with slang that makes sense for your usage and coding patterns. To convince people that your jQuery dialect is great, please publish your plugin at plugins.jquery.com and evangelize it somewhere (other than the bug tracker). The jQuery core file is not the plugin repository.

Changed February 22, 2013 07:54PM UTC by Panman8201 comment:9

Replying to [comment:8 dmethvin]:

Even though the documentation says this?

I don't want to drag this out too much farther because I understand this won't make it anywhere. But I'd like to clarify this point.

The documentation is clear once you read it, no problem there. However, when I first looked at the function signature I read it as "return the next sibling that is of type.." Ex: .**next**(**".divider"**) However, it's actually "return the adjacent (next/previous) sibling IF it is of type.." So to me the "natural english" of the signature is off. Again, IMO.

Thanks again for the feedback. I appreciate a response, even if it's not one I want to hear.