Jump to content

Page-equivalent for jQuery closest()


mindplay.dk
 Share

Recommended Posts

I often need to find the closest parent Page of a particular type, and I found that something equivalent to jQuery's closest() function was not available.

It's a simple addition to the Page class:

/**
 * Return this page's closest parent matching the given selector.
 *
 * If no parent matches the given selector, this function returns a NullPage.
 *
 * @return Page
 *
 */
public function closest($selector) {
 $parent = $this->parent();
 while($parent && $parent->id) {
  if ($parent->matches($selector)) {
   return $parent;
  }
  $parent = $parent->parent();
 }
 return new NullPage();
}

Please consider including this in the next release.

  • Like 4
Link to comment
Share on other sites

What might be good is if we just add $selector support to the $page->parents() function so that you could do: $page->parents($selector). Though, keeping consistency with jQuery, adding closest() might make sense as it returns a single item rather than an array (as parents() would). I'd never used closest() in jQuery before, but this page describes the difference (scroll down a bit): http://api.jquery.com/closest/ … I think it seems like a good idea.

Link to comment
Share on other sites

I've used closest() a few times rather than parent()->parent() repetitively, or if there was ever a chance the layout would change slightly on the page but I knew that somewhere back up the tree a particular element would always exist in relation to the current one.

It probably wouldn't be used all that much in PW since another layer of pages would rarely be added between the current page and the parent page, but saying that I'm sure I've run into this before myself so it may as well go i and I'll try and find where I've seen that code where I needed to do something similar.

A bit off topic, but ryan: have you at any point emailed anyone at jQuery about PW? I think they'd be interested in the concept :)

Link to comment
Share on other sites

Actually, my first take was an extension of parents() adding a $selector argument - it turns out that's probably not very useful, as this would potentially give you multiple parents.

When would you need only certain pages from the trail of parents, skipping some of them? Seems like an edgy use-case. And if you do want filtered parents, you can already do $page->parents->filter('template=xyz')

Also, if you're looking for one specific parent, there's no need to traverse the parents all the way to the top, if the parent you're looking for is closer than that - hence the function-name, closest() ...

Link to comment
Share on other sites

Actually, my first take was an extension of parents() adding a $selector argument - it turns out that's probably not very useful, as this would potentially give you multiple parents.

$found = $page->parents->find("template=basic-page")->reverse()->first();

This would be the equivalent of closest return only 1 page, and performance-wise also fast as it won't need to query for 1000s pages anyway.

Would be nice addition to have a shorter version if needed anyway.

Just got me curious. For what task are you using closest method so often?

Link to comment
Share on other sites

Just got me curious. For what task are you using closest method so often?

Finding a parent with a particular template, typically... very useful if your site is divided into major areas, e.g.:

area 1
 page 1
 page 2
area 2
 page 3
 page 4
   page 5
   page 6

Suppose you're on page 5 and need a title or image for that area of the site... $page->closest('template=area')->image->... etc.

Link to comment
Share on other sites

I'm not a fan of rootParent, and I think there's a reason why jQuery has no equivalent to that - the exact type of page you're going to get, is unpredictable.

Not in the sense that you won't always get the parent below the root of course, but in the sense that the root-parent could change - because pages can be moved around.

And in some cases that's what you want - e.g. for breadcrumbs... but in the "site area" situation, the site could grow, and what currently happens to be a root-parent, could become a sub-area of another new area, some day...

Link to comment
Share on other sites

Yeah that's what I meant. Though never came across having to structurally change a website drastically like this anyway. Not very good regarding SEO and searchengines, so a structure defined will stay the way it is anyway.

Link to comment
Share on other sites

When using multisite module "closest" would also be very handy. There the rootParent is actually the "root" etc. Of course parents("selector-here") would work too.

Also I have several cases where I count for how many parents page has and based on that return ->eq(1) or ->eq(2). I think in many cases closest would have been helpful.

Link to comment
Share on other sites

This all makes sense, I'll plan to get the closest() method added. The only thing I'm having a little trouble with is the term "closest", as to me it would imply not just parents, but siblings, children, etc. I realize it's just referring to parents in jQuery, but still question their choice of the term. Would it not be better for us just to support a $selector argument on the parent() method, like $page->parent($selector)? Seems like it would do exactly the same thing, but maybe be a little more readable? Maybe I just haven't used this particular method in jQuery enough. But I like the goal behind the closest() method, regardless of what we call it.

I tend to use rootParent quite a lot in my own sites, for any number of things. But then again, all my sites are structured in a manner that the first level pages are always the main section pages… and that never changes. So rootParent and me are good friends. :)

Link to comment
Share on other sites

Too long? There is also a rootParent()! And if closestP() then leave out the P anyway. :P Or closestParent() and like outputFormatting() has a of(), why not make a cp() too? :D

I use closest a lot in jquery once I understood what it does and it wasn't suggesting to me it would be for parents bubbling up the dom tree. So a little like Ryan said. I don't think it will hurt

Link to comment
Share on other sites

closestParent() makes the most sense to me. Though I'm still wondering about just: parent($selector); because the parent() function already implies closest parent (given that it returns the direct parent of the page). So if you provide a selector to it, then it's like saying: "closest parent that has [something]." I also like that extending the existing parent() function in this way wouldn't involve adding a new function to the API. It would also make it consistent with how the child($selector) function works.

jQuery uses parent(selector) for something that isn't applicable to ProcessWire. jQuery's parent() can return multiple items, and the selector filters those. So they didn't have a choice to use the parent() function for this purpose. In our context, we don't actually need a separate function like they do.

  • Like 2
Link to comment
Share on other sites

There's something to be said for consistency with jQuery though - since it's basically ubiquitous and familiar to any developer.

I would not be opposed to enhancing parent() though, I'm starting to think maybe that makes the most sense - since it already basically works like we're describing, as far as selecting a single parent... And the API would be backwards compatible, since not passing a selector would mean "anything goes", consistent with the rest of the PW API.

(come to think of it, sometimes after not using JQ for a few months, I'll instinctively try .parent() for what we're discussing - it seems logical... but maybe that's just me...)

Link to comment
Share on other sites

Though I'm still wondering about just: parent($selector); because the parent() function already implies closest parent (given that it returns the direct parent of the page). So if you provide a selector to it, then it's like saying: "closest parent that has [something].

I would agree with this if the jQuery inspiration wouldn't be advertised on PW's homepage. In jQuery, something like parent($selector), would mean that you are selecting only $selector from all the results returned by parent(). This could bring some confusion to jQuery users starting with PW.

I noticed that parents() works on PW to get all the ancestors of a page. I would say parents($selector) makes all the sense.

  • Like 1
Link to comment
Share on other sites

I'm thinking we'd add the same thing to both parents() and parent(), except that parents() would return all matching, while parent() would return the first (closest) one. That way you always know you'll get a PageArray() from parents() and a Page from parent(). I'm not too concerned about maintaining consistency with jQuery, as their context is still very different from ours… different enough that strict adhesion to their API syntax would ultimately be confusing if taken too far in our context. ProcessWire takes inspiration from jQuery where it makes sense, but cloning their API is not the goal. Rather, we want to provide the clearest API for ProcessWire within our context, regardless of what jQuery is doing. The environments they operate in are very different. In terms of how you work with it, ProcessWire probably has more in common with YUI3 than jQuery, given the more specific single vs. multiple types in YUI3. But I still prefer jQuery as our syntax starting point, rather than ending point.

Link to comment
Share on other sites

I would agree with this if the jQuery inspiration wouldn't be advertised on PW's homepage. In jQuery, something like parent($selector), would mean that you are selecting only $selector from all the results returned by parent(). This could bring some confusion to jQuery users starting with PW.

Very good point, unfortunately.

Link to comment
Share on other sites

Not really worried about that. We advertise jQuery "inspiration" not "duplication". But like I say the goal has never been to clone their API… nor would it be logical to, given the very different contexts. Instead, I think the goal should be to make ProcessWire's API as simple, easy and logical as possible for the place that ProcessWire works in.

I actually thought that jQuery had quite a sweet spot in their API right around 1.2.6 (or thereabouts). Since that time, I think the balance has shifted a bit towards complexity in complexity vs. power balance. But then again, I'm not a full time jQuery user, so there's a lot in their API that I rarely use.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...