Dogstudio / highway

Highway - A Modern Javascript Transitions Manager
https://highway.js.org/
MIT License
1.43k stars 92 forks source link

Change renderer to append new view, and then remove old view. #39

Closed joshkirk-zero closed 5 years ago

joshkirk-zero commented 5 years ago

I don't expect you to merge this one for a release, but I thought you would find it interesting and perhaps others would find it useful. Also, if you want to merge it for a release I would love that 💯

I'm a big fan of what you've built with highway and I'm a huge PJAX nerd. But as you know based on my past pull request, it was missing a couple crucial things that I needed. I added the contextualTransitions, and now I changed it so instead of removing the innerHTML of the view, and changing the data-router-view attribute, it first appends the new view after the old one, then removes the old one. This is crucial in order to have a transition that keeps elements from the previous view until the new view elements are ready to take it's place. This solves https://github.com/Dogstudio/highway/issues/17.

So in summary, views still have their transitions assigned, as well as a default transition of course. Contextual transitions can fire on click, and now the new view is appended to the data-router-wrapper before the old view is removed. Then the old view is removed. The reason for this is to achieve visual trickery. Sometimes you want an element that appears to persist across views. See this video of an early WIP to see what I mean: https://zero-1.wistia.com/medias/sfmck2lxbw... when you get to the bottom of a project, you click to go to the next project. The large text appears to stay and carry over to the next view, but in reality the next view just has the same text positioned in the same way.

Before this wasn't possible, since there would be a flash of emptiness in-between the old content being removed and the new content added. Enjoy!

joshkirk-zero commented 5 years ago

Here's another vid of both a contextual transition (panel to hero transition), and the overlapping views (the panel image and the hero image appear to carry over, but really it's one view swapping with another). https://zero-1.wistia.com/medias/g91oa87zo2

ThaoD5 commented 5 years ago

Hey @joshkirk-zero ! Nice method and nice work ! I think your approach is really interesting and straight forward, in my opinion it answers correctly the need for some users to overlap old view / new view.

What I wonder is if we shouldn't have a "toggle" when initing Highway in order to have the overlapping or just the view replacing like we have now, the use cases are quite different and we're probably gonna discuss that with @Anthodpnt along with seriously considering merging your work into Highway ! 🥇

Thanks for coming up with a working solution that we can test directly, we're gonna let you know the next step !

Best Regards, Thao

Anthodpnt commented 5 years ago

HI @joshkirk-zero,

Huge thanks for this pull request ! We will definitely take it into consideration. I checked your commit, did it really take only 2 lines of code to make the overlapping possible ?

joshkirk-zero commented 5 years ago

Indeed, actually -3 LoC after removing the remove() function altogether and not needing to swap attribute. But moreover I think it just took a fresh frame of mind. I was familiar with your setup in this repo after first working on the transitions piece. Then I used Highway on a couple actual sites that are launching soon, so I better understood the library itself. That made it much easier to dive right in this time and make this small adjustment.

Re: @ThaoD5's point about the methods of swapping being much different, this is true in terms of approach but the difference is imperceptible.

It boils down to this: you either want

In both cases, this method of appending the new and then removing the old makes no difference to the user experience but to the browser, the content overlaps long enough to avoid flashing of emptiness when not using a page loader.

Note that this requires a data-router-wrapper element since that's where the data-router-view swaps in and out. I noticed the _site in this repo was actually operating without a data-router-wrapper so I added that in the template.

It is with this freedom to fire any transition you want on the fly and not use a page loader, combined with the rest of Highway's modern architecture that makes this the ultimate PJAX library for any use.

joshkirk-zero commented 5 years ago

But that just, like, my opinion man (https://www.youtube.com/watch?v=pWdd6_ZxX8c). ;)

Here's a better video demonstrating a use case: https://zero-1.wistia.com/medias/sfmck2lxbw

There's a basic fade transition applied, but the next project link in the project footers have data-transition="NextProject" in order to fire that contextual transition. When clicking any other link such as the logo to return to the homepage, it uses the view transition.

And here is the code for the contextual transition NextProject: https://gist.github.com/joshkirk-zero/6de92a804cd7370f5002215e17c2a90b

Anthodpnt commented 5 years ago

@joshkirk I'm not sure to understand, when I see the code, the add method append the new view but remove the old one at the same time... How do you manage the overlapping of both views then ?

Anthodpnt commented 5 years ago

@joshkirk I ran some tests and I can't see the trick here. What bothers me is that now if you log the view from the in and out methods in your transition you have the exact same code and that's a problem IMO because in the out transition you loose the context, you get the code of the new view instead of the old one. Is it part of the trick to overlap both views ?

joshkirk-zero commented 5 years ago

As far as swapping them at the same time in add, doing so (as opposed to removing the innerHTML and then placing new HTML separately) makes the experience consistent across browsers + mobile. This tiny difference in timing allows the new container markup to take on styles and get positioned before the old one gets removed. From there, JS or CSS takes over and animates the new elements in however you want.

Gah! Your second comment confused me for a few minutes here. Your console is actually lying to you though. When you click the element that gets logged, the console gets confused and tries to update the element that it logged. You can see the different views being logged if you console.log(view.firstElementChild) instead of console.log(view) in the in and out transition. So basically, anything you wanted to run/detach from the old view would still work because the context is still there.

joshkirk-zero commented 5 years ago

This was so bizarre, something I had not experienced before so I had to take a video! https://zero-1.wistia.com/medias/nesnkr9y85

You can see that the console.log outputs actually change if the elements that they're logging get changed. So when we log the wrapper, it appears that the markup is the same because the console updates the child before we can click into it. When we log the view itself, this shows they are really separate elements.

Anthodpnt commented 5 years ago

Ok perfect, thanks for the explanations. I'll dive into your code to see if I can improve it.

Instead of having the data-router-wrapper as the view parameter of the transition methods I still want to get the data-router-view to simplify. I also think I'll add classnames to both views in order to simplify the CSS so user will be able to target .new-view and .old-view.

Finally I can improve the add method a bit because so far if users are putting some extra code above or below the data-router-view it might break the script. They are not supposed to do that but it's important to take this into account and avoid useless Github issues.

joshkirk-zero commented 5 years ago

Sorry I missed your last comment somehow..


Instead of having the data-router-wrapper as the view parameter of the transition methods I still want to get the data-router-view to simplify.

Yes this makes sense, I agree.

I also think I'll add classnames to both views in order to simplify the CSS so user will be able to target .new-view and .old-view

This would be nice too! In the past, when I needed to differentiate I would always do something like .view:last-child since I've always operated under the pattern that the only thing in the data-router-wrapper are the 1-2 data-router-views.

...if users are putting some extra code above or below the data-router-view it might break the script. They are not supposed to do that but it's important to take this into account and avoid useless Github issues.

If you can make that work, fantastic. But I would recommend not accommodating people that don't understand the basics of PJAX architecture though. Once you go down that path, you open yourself up to a lot more basic questions/difficulties. (See: https://github.com/luruke/barba.js/issues?q=is%3Aissue+is%3Aclosed)


If you need any help or would like to chat I'm available 👍