Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
peterPotter

ScrollToPlugin doesn't work with shadow dom

Recommended Posts

I am using the Polymer library to build a web app, using web components. I am trying to scroll to a particular div, but since the div is inside a shadow dom, GSAP's ScrollToPlugin doesn't reach inside shadow dom to find elements, so it doesn't work.
 
Browsers' native scrollIntoView Works
Some browsers have the scrollIntoView method, and for whatever reason, this is the only solution that is working for me. But of course it just jumps to the div; it doesn't scroll. I want to scroll gracefully.
 
Question
I want to use GSAP's ScrollToPlugin to scroll my page to a div that is inside a shadow dom. A correct reference to a shadow dom  (using Polymer) looks like this:

var myDiv = document.querySelector('custom-element /deep/ #myDiv'); 

// this works (native browsers' scrollIntoView):
​ myDiv.scrollIntoView(); 
Important Note
I am pretty sure the browser's native scrollIntoView method is not using element.scrollTop. The reason is that every solution that uses scrollTop doesn't work. The element.scrollTop property is always returning 0, since it sees the shadow dom as the root document.
 
I tried a couple of jQuery plugins and none of them works. They all use scrollTop (just as the GSAP's ScrollToPlugin does).
 
Whatever solution the browser's native scrollIntoView method uses seems to be the best way to scroll into view for elements within a shadow dom. 
 
Who can help me with this?
Link to comment
Share on other sites

You're supposed to be able to select shadow dom elements like this

this.$.shadowElement;

But I can't get it working outside of Chrome. See this example.

http://plnkr.co/edit/0fJl0cmK6iSUrQw7KuJe?p=preview

 

Are you using anything besides Polymer? With Angular it's pretty easy to hook into Polymer, so I know it's possible.

 

What are you trying to scroll to? You might be able to just copy and create your own version of the element if it's not complicated.

  • Like 2
Link to comment
Share on other sites

Thanks for the reply, OSUblake. 

 

Your example is different from the issue I reported. TweenMax is working fine with Polymer for everything besides scrolling with the ScrollToPlugin. The main issue is that the ScrollToPugin doesn't work when trying to scroll to a div located in a shadow dom (because element.scrollTop, which the ScrollToPlugin uses, is always 0).

 

Selecting an element is also not a problem. I can successfully use different ways, including the automatic-node-finding option you showed, to find or select a node.

Link to comment
Share on other sites

So the solution is to iterate over the element's parents (piercing through the shawdow dom) until you get the eventual distance from the top of element to the window (scrollTop). Here is a jQuery pull request to fix the issue for the jQuery's offset method, which has the same problem that GSAP's ScrollToPlugin has:

https://github.com/jquery/jquery/pull/1976/files

 

The crux of the matter is this bit of code (Jack, if you do the same for the ScollToPlugin, that would be fantastic):

// We have element in ShadowDOM, need recursive traversal from element to its parent
if (elem.matches && elem.matches(":host *")) {
// Make sure it's not a disconnected DOM node
while (true) {
elemParent = elemParent.parentNode || elemParent.host;
if (elemParent === doc) {
break;
} else if (!elemParent) {
return box;
}
}
} else {
// Make sure it's not a disconnected DOM node
if (!jQuery.contains(docElem, elem)) {
return box;
}
}
Link to comment
Share on other sites

Oops! I just tested the code I last posted and I noticed that the solution works on only native shadow-dom browsers. We have to use Polymer's polyfill library for selectors like :host * to work on non-native shadow-dom browsers. I will whip a solution for this that works inside Polymer. 

Link to comment
Share on other sites

I had a hard time understanding your question because you responded back that selecting an element was not the problem. If you could select the element, then you could figure out its position. Were you using jQuery to do this?

 

Not that it matters now, but I created a scroll to demo to try and figure out what the issue was. I'm guessing you were trying to get the position inside some deeply nested elements because I had no problems going down 1 level. Oh well, glad you figured it out!

 

http://plnkr.co/edit/czdXVFv9tojP3B1Ac0Ds?p=preview

  • Like 2
Link to comment
Share on other sites

OSUblake, Thanks very much for taking the time to create the thorough example. You helped me find the solution. This method, elem.getBoundingClientRect(), which I have never seen or heard of before, was key to the solution. However, that alone didn't solve the problem only because we still had to subtract the distance from the top of the element to the top of the window, which the following function (credited to http://javascript.info) does:

function getOffsetRect (elem) {
var box = elem.getBoundingClientRect(),
body = document.body,
docElem = document.documentElement,
scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop,
scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft,
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
top = box.top + scrollTop - clientTop,
left = box.left + scrollLeft - clientLeft;

return { top: Math.round(top), left: Math.round(left) }
}

And since that function gets the correct scrollTo distance, we can use TweenMax's ScrollToPlugin (or any other scrollTo library or plugin) easily.

  • Like 1
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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×