WICG / navigation-api

The new navigation API provides a new interface for navigations and session history, with a focus on single-page application navigations.
https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigation-api
484 stars 30 forks source link

feature: Retrieve the element (if any) triggering the navigation #238

Closed VicGUTT closed 2 years ago

VicGUTT commented 2 years ago

Retrieve the element (if any) triggering the navigation

Playing around with the Navigation API, there doesn't seem to be, as of now, a way to retrieve the element triggering the navigation.

The NavigateEvent event provides us a userInitiated property which allows us to determine if the navigation was triggered by a user action (click, form submit, ...) or programatically (navigation.navigate(), el.click(), ...). But we lack a way to determine what triggered the navigation.

Background/Motivation

My interest comes from creating SPA-like feeling MPAs. Meaning, traditional server driven apps, where every request returns a fully or partially formed HTML document (instead of JSON). The SPA-like feeling would come from making XHR/fetch requests once a navigation/resource is requested (via a clicked link or form submission) and on response, swap out the old document for the new one, or morph the new content into the old one (using a library like morphdom).

Use cases

Consider a visit library that you'd install from NPM to SPA-ify your app.

You could use it to make visits, aka navigations in one of two ways:

Directly from your JS

import visit from 'visit';

visit.get('/about'); // <- a glorified `navigation.navigate()`
visit.put('/post/3', { headers: {...}, body: new FromData(el) ); // <- `navigation.navigate()` + configure the underlying fetch request
visit({ url: '/post/3', method: 'DELETE', headers: {...}, ... })

Directly from your HTML

<a href="/about" data-visit>About page</a>

<form action="/post/3" data-visit data-visit-method="PUT" data-visit-headers="{...}">
    ...
</form>

<button data-visit="{ url: '/post/3', method: 'DELETE', headers: {...}, ... }">
    ...
</button>

And the underlying library code could be roughly similar to:

function visit(params) {
    // ...

    navigation.addEventListener('navigate', (e) => {
        const element = e.targetElement; // <- Element or null

        if (!element || !element.hasAttribute('data-visit')) {
            return;
        }

        e.transitionWhile(
            fetch({
                url: element.getAttribute('data-visit-url') || determineUrlToUseFromElement(element),
                method: element.getAttribute('data-visit-method') || determineMethodToUseFromElement(element),
                // ...
            })
            .then(response => response.text())
            .then(html => swapDom(html))
        );
    });
}

Without having a targetElement (or whatever the name would be) property on the event, the declarative feature of customizing the request throught HTML attributes would only be possible by attaching an event to every elements individually, which would negate the "centralized router" of the API.

domenic commented 2 years ago

Dupe of https://github.com/WICG/navigation-api/issues/225?

VicGUTT commented 2 years ago

Wow yes, sorry about that. Couldn't find any similar issues for some reason. Will continue the conversation over there :v: