w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[cssom-view] Consider adding Element.scrollParent #1522

Open oriadam opened 7 years ago

oriadam commented 7 years ago

Suggestion: Element.scrollParent Returns the closest element which controls the position of current element via a scroll.

This feature is useful in many cases, easy for browsers to implement and hard for plugins such as jQueryUI to get it right.

Link to relevant spec: https://www.quirksmode.org/dom/w3c_cssom.html#t33 Link to current workaround by jQueryUI: https://api.jqueryui.com/scrollParent

Thanks

upsuper commented 7 years ago

Could you describe exactly what you really want? And include some usecases for this? Also, would it be useful to handle horizontal and vertical scrolling differently?

I checked the "workaround" in jQuery UI, and it seems to me that it just checks the value of overflow? Is that enough for the usecases? I mean, having overflow: auto doesn't mean the element would have scrollbar, let alone whether it would be scrollable. (Actually having overflow: hidden doesn't even mean the element being not scrollable given that author can implement custom scrollbar with setting overflow to hidden.)

Also note that, there are several issues with the function in jQuery UI as far as I can tell:

  1. It assumes that an element with position: absolute wouldn't be scrolled by any static ancestor, which is false, because when you don't specify top/bottom/left/right, the element would be attached to where it would be if it is static-positioned, and thus would be scrolled by a static ancestor.
  2. In addition to the case above, absolutely-positioned and fixed-positioned elements would also be scrolled by ancestor which "acts as a containing block for absolutely positioned and fixed positioned descendants". See CSS Containment spec for example.
  3. Except the cases above, fixed-positioned element shouldn't have scroll parent otherwise. It should really return null rather than the document.

And actually, this isn't going to be "easy" for browsers to implement. Browsers may end up just having a function like what you would have in JavaScript, which tries to walk up through the element tree and check relevant properties. The only thing which may make browsers be able to handle more elegantly is that they can reuse some checking logic with some existing code. But that really depends on how this would be specified.

upsuper commented 7 years ago

It assumes that an element with position: absolute wouldn't be scrolled by any static ancestor, which is false, because when you don't specify top/bottom/left/right, the element would be attached to where it would be if it is static-positioned, and thus would be scrolled by a static ancestor.

I was wrong on this. Even if the element would be positioned at where it would be if it's static, its static ancestors are still not able to scroll it.

zcorpan commented 7 years ago

Do you want this API to return the nearest ancestor that is https://drafts.csswg.org/cssom-view/#potentially-scrollable ? How should it interact with shadow DOM?

oriadam commented 7 years ago

It should return the same element that is affected when calling Element.scrollIntoView

On Tue, Jun 13, 2017 at 4:18 PM, Simon Pieters notifications@github.com wrote:

Do you want this API to return the nearest ancestor that is https://drafts.csswg.org/cssom-view/#potentially-scrollable ? How should it interact with shadow DOM?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/w3c/csswg-drafts/issues/1522#issuecomment-308112506, or mute the thread https://github.com/notifications/unsubscribe-auth/ACqFOd4VpXqpNQ-gVvJsLY8W_jHW9vpJks5sDowhgaJpZM4N2g-7 .

-- מנסים טבעונות ל-22 יום: http://veg.anonymous.org.il/etgar22

zcorpan commented 7 years ago

This is blocked on https://github.com/w3c/csswg-drafts/issues/1526 https://github.com/w3c/csswg-drafts/issues/1527

markcellus commented 7 years ago

I posted a reply on the www-dom mailing list, but I'll leave it here also...

This sounds like a cool feature but I'm curious about the use-cases as I've never come across a situation where I've needed this. Are there not any cases where there may be two parents in the hierarchy and you don't want the closest, but the one after it? Also, a more specific name would be better like closestScrollParent or similar. scrollParent is vague since there could be multiple "scrollParent"s technically (i.e. grandparents, great grandparents, etc :grin: )

oriadam commented 7 years ago

You are right, nested scroll-enabled elements might be needed. Perhaps scrollParent and scrollParents

Here is a use case example: https://jsfiddle.net/oriadam/n53asLs2

On Sun, Jun 25, 2017 at 5:40 PM, Mark Kennedy notifications@github.com wrote:

I posted a reply on the www-dom mailing list, but I'll leave it here also...

This sounds like a cool feature but I'm curious about the use-cases as I've never come across a situation where I've needed this. Are there not any cases where there may be two parents in the hierarchy and you don't want the closest, but the one after it? Also, a more specific name would be better like closestScrollParent or similar. scrollParent is vague since there could be multiple "scrollParent"s technically (i.e. grandparents, great grandparents, etc 😁 )

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/w3c/csswg-drafts/issues/1522#issuecomment-310906419, or mute the thread https://github.com/notifications/unsubscribe-auth/ACqFOUWtuoPZzYgZCfB2TjTlHg9C61uEks5sHnFRgaJpZM4N2g-7 .

-- מנסים טבעונות ל-22 יום: http://veg.anonymous.org.il/etgar22

markcellus commented 7 years ago

I checked the "workaround" in jQuery UI, and it seems to me that it just checks the value of overflow? Is that enough for the usecases? I mean, having overflow: auto doesn't mean the element would have scrollbar, let alone whether it would be scrollable. (Actually having overflow: hidden doesn't even mean the element being not scrollable given that author can implement custom scrollbar with setting overflow to hidden.)

@upsuper that's weird. I haven't looked at the jQuery UI code, but that seems very problematic. Wouldn't it be simpler to determine if an element is "scrollable" by just doing

if (parentElement.clientHeight < parentElement.scrollHeight) {
   // element is scrollable parent
}
jonjohnjohnson commented 6 years ago

This is the type of polyfill I've had to do for el.scrollRootY/el.scrollRootX more than once.

function scrollRoot(el) {
  var roots = {}
  roots.x = undefined;
  roots.y = undefined;

  function checkScroll(el) {
    if (!el.tagName || el === document.documentElement) {
      roots.x = roots.x || document;
      roots.y = roots.y || document;
      return;
    }
    var parent = el.parentElement;
    var regScroll = /(auto|scroll|overlay)/;

    if (parent.scrollHeight > parent.clientHeight && regScroll.test(window.getComputedStyle(parent).overflowY) && !roots.y) {
      roots.y = parent;
    }
    if (parent.scrollWidth > parent.clientWidth && regScroll.test(window.getComputedStyle(parent).overflowX) && !roots.x) {
      roots.x = parent;
    }
    if (!roots.x || !roots.y) {
      checkScroll(parent);
    }
  }
  checkScroll(el);
  return roots;
}

Keep in mind, this doesn't check for a overflow:hidden in either axis, which is programmatically scrollable, if not user scrollable. As well as not checking for position:fixed which moves an element outside it's scrolling context, or back in when a transform on an ancestor then takes the place of the initial containing block. But it could still structurally be inside a nested scrolling element and could then accept scrolling/pointer events for the nested scrolling element? Just saying this stuff gets fun, especially between mouse and touch environments.

And after that, adding scroll listeners, where you still take into account compatibility issues of documentElement and scrollingElement, as well as finding the correct scrollY/scrollX when the target of the event comes from the document, etc... it's hairy. Personally, I hate having to support scrolling anything besides an actual element (I wish html simply had the outermost scrolling mechanism).

PS @mkay581 @oriadam @upsuper If something like this is spec'd, I don't see the need for "grandparents" or "closest", when one could access the scrollRoot of an elements scrollRoot, all the way up if need be.

PPS @upsuper

And actually, this isn't going to be "easy" for browsers to implement.

Don't browsers already have a solid understanding of this for features like sticky and SIV in how scrolling chains from their target? Making this as *simple as exposing a targets "scroll context" to devs?

oriadam commented 5 years ago

Use cases I personally needed (and used jQueryUI for them):

  1. Sticky th elements must have a very accurate top/bottom value to work properly. I used scrollParent to calculate the correct values.

  2. Infinite scroll based content widget. On mobile the widget is inserted directly to the body, but on desktop it is inserted inside a scrollable div. I used scrollParent to attach the events to load the list.

  3. Video player that pauses when scrolled out of view. The video player can be included anywhere, so it must use scrollParent to attach the proper events and check if it is in view, and how far down/up it is scrolled away.

  4. Lazy loading ad widgets.

bramus commented 4 days ago

This addition would be very helpful.

Given that document.scrollingElement exists, maybe the name should be scrollingParent or scrollingParentElement for consistency reasons?