Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explain how to use # for scrolling to an id on a sub page #56

Open
kenchris opened this issue Mar 27, 2016 · 12 comments
Open

Explain how to use # for scrolling to an id on a sub page #56

kenchris opened this issue Mar 27, 2016 · 12 comments

Comments

@kenchris
Copy link

I can have a route like /article/1#subsection

It would be nice with a explanation on how to load that route and still make it scroll to the #subsection like it would had I used actual pages instead of routing.

@cdata
Copy link
Contributor

cdata commented Mar 29, 2016

Is this purely documentation? I'm not sure we have an implemented strategy for this yet.

/cc @rictic

@e111077
Copy link
Member

e111077 commented Mar 29, 2016

@cdata that's a good point; I'm pretty sure this will require some implementation.

@rictic
Copy link
Collaborator

rictic commented Mar 29, 2016

We haven't proved one out enough to have a recommended strategy, but it may be very straightforward.

e.g. If you're not using the hash as the path then you could observe the hash of an <iron-location> and use this.$$('#' + hash).scrollIntoView()

@arthurevans
Copy link
Contributor

I think it may be something that needs to be handled by the app (or user's element, anyway), but we should talk about patterns.

In particular, if you're loading data dynamically, you need to wait until the data is loaded & rendered, then scrollIntoView. And the ID you're scrolling to could be in light DOM, local DOM, or even a child's local DOM. So you may need to know the structure of the page in order to scroll intelligently.

For example, in iron-doc-viewer, we currently use Polymer.dom.flush() to ensure that the dom-repeats have rendered out before we call scrollIntoView. This may not be ideal, but it works.

https://github.com/PolymerElements/iron-doc-viewer/blob/master/iron-doc-viewer.html#L211

@cdata
Copy link
Contributor

cdata commented Mar 31, 2016

It would be good to verify the behavior of using # to target a scroll point in the page under ShadowDOM. In the Polymer Elements case, we certainly have many duplicate IDs across the several shadow boundaries. Does the browser treat these as if they are all at the top level?

@arthurevans
Copy link
Contributor

Yeah, it doesn't work. Or at least it doesn't work consistently. You can try this little jsbin here:

http://output.jsbin.com/piriwa#3

In shady DOM, #inner-4 scrolls to card #4, #generic scrolls to the first card (each card has a div with the id generic).

In native shadow DOM, only the IDs that aren't contained in any shadow root work at all. (#4 works but not #inner-4).

So in most cases I suspect one of the elements is going to have to handle this themselves. unless:
a) the IDs are in light DOM, and b) the content is already loaded.

@cdata
Copy link
Contributor

cdata commented Mar 31, 2016

I wonder if this is an opportunity for us to define specialized semantics for how hash links should work in an app that has several layers of ShadowDOM. Perhaps we could prescribe a pathing mechanism that users can leverage to specify jump points through a series of shadow boundaries. For example:

  1. User navigates to /lur#omicron#persei#8

  2. Target selection is made as follows (approximation):

    let path = window.location.hash.slice(1).split('#');
    let element = path.reduce(function(host, id) {
      let target = host.shadowRoot ? host.shadowRoot.querySelector(`#${id}`) : host;
      return target ? target : host;
    }, this);
  3. Target element is scrolled into view (as needed / possible).

This is just a random idea. Maybe not ideal because we're inventing semantics that depart from the platform. @rictic WDYT?

@rictic
Copy link
Collaborator

rictic commented Apr 1, 2016

@cdata That's clever. I like that the reverse function to generate the hash for a node is similarly straightforward.

Advantages:

  • simple and easy to understand
  • polymer-agnostic, cuts with the grain of the platform primitives and not against them
  • doesn't prevent or clash with a custom scrollIntoView system

Disadvantages:

  • can only link to elements that are in an unbroken chain of elements with ids
  • urls could get long
  • adds (a probably small amount) of code

@notwaldorf
Copy link
Contributor

👍 to @cdata's proposal

@arthurevans
Copy link
Contributor

It feels to me like @cdata's proposal cuts against the grain of the primitives. It also feels like it breaks <carbon-route>'s notion of modular and encapsulated routing by making scroll-to-anchor cut though all those carefully-constructed encapsulated elements.

Also, how do you know which element you have to scroll? And how do you know that the page has rendered enough so that you can scroll to the ID? What if it's a big honking page that takes several frames to load and render?

In these cases, I'd argue, you want the lowest level component that understands scrolling to deal with it.

That said, I don't have a better proposal for an overarching solution. I thought of an event-like scheme where individual components can handle and cancel propagation of the change—but pretty sure that falls apart because data binding can't propagate that way.

Also considered each route having a handlesHashWhenActive flag, but again, there's no real notion of precedence or which route "wins". Because everything's propagated though data binding, I don't think there's any notion of which route is "deepest", for example.

@arthurevans
Copy link
Contributor

arthurevans commented May 6, 2016

So, um, I guess for the doc site we'll fall back to "application listens to hashchange, decides which component should manage the scroll state for this event" which is basically what @rictic said way back in #56 (comment). 🐐

@lorenzleutgeb lorenzleutgeb mentioned this issue Jun 7, 2016
@evilj0e
Copy link

evilj0e commented Dec 17, 2016

It's awfull way, but you can do something like that:

      ready: function() {
        this.shadowRoot.addEventListener("dom-change", function(event){
            var elementToFocus = this.getElementById(window.location.hash.slice(1));
            if (elementToFocus) {
              elementToFocus.scrollIntoView();
            }
        });
      },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants