Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import { navigator } from '@hotwired/turbo'
import { Controller } from 'stimulus'
import { useMutation } from 'stimulus-use'
export default class extends Controller {
connect (): void {
useMutation(this, { attributes: true })
}
mutate (entries: MutationRecord[]): void {
entries.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
const src = this.element.getAttribute('src')
if (src != null) { navigator.history.push(new URL(src)) }
}
})
}
}
@fffx

This comment has been minimized.

Copy link

@fffx fffx commented Jan 27, 2021

// js
import { Turbo } from 'turbo'
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    this.observer = new MutationObserver((mutationsList) => {
      mutationsList.forEach((mutation) => {
        if (mutation.type === "attributes" && mutation.attributeName === "src") {
          history.pushState({ turbo_frame_history: true }, "", this.element.getAttribute("src"))
        }
      })
    })

    this.observer.observe(this.element, { attributes: true })

    this.popStateListener = (event) => {
      if (event.state.turbo_frame_history) {
        Turbo.visit(window.location.href, { action: 'replace' })
      }
    }

    window.addEventListener("popstate", this.popStateListener)
  }

  disconnect() {
    this.observer.disconnect()
    window.removeEventListener("popstate", this.popStateListener)
  }
}
@julianrubisch

This comment has been minimized.

Copy link

@julianrubisch julianrubisch commented May 6, 2021

Here's a version using the ingenious stimulus-use package, reads a bit nicer:

import { Turbo } from "@hotwired/turbo-rails";
import { Controller } from "stimulus";
import { useMutation } from "stimulus-use";

export default class extends Controller {
  connect() {
    useMutation(this, { attributes: true, childList: true, subtree: true });
  }

  disconnect() {
    window.removeEventListener("popstate", this.popStateListener);
  }

  mutate(entries) {
    for (const mutation of entries) {
      if (mutation.type === "childList") {
        // re-initialize third party, e.g. jquery, plugins
      }

      if (mutation.type === "attributes" && mutation.attributeName === "src") {
        history.pushState(
          { turbo_frame_history: true },
          "",
          this.element.getAttribute("src")
        );
      }
    }

    this.popStateListener = event => {
      if (event.state.turbo_frame_history) {
        Turbo.visit(window.location.href, { action: "replace" });
      }
    };

    window.addEventListener("popstate", this.popStateListener);
  }
}
@Intrepidd

This comment has been minimized.

Copy link
Owner Author

@Intrepidd Intrepidd commented May 6, 2021

Actually here is my latest version using the newly available navigator API + stimulus use ;)

import { navigator } from '@hotwired/turbo'
import { Controller } from 'stimulus'
import { useMutation } from 'stimulus-use'

export default class extends Controller {
  connect (): void {
    useMutation(this, { attributes: true })
  }

  mutate (entries: MutationRecord[]): void {
    entries.forEach((mutation) => {
      if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
        const src = this.element.getAttribute('src')
        if (src != null) { navigator.history.push(new URL(src)) }
      }
    })
  }
}
@julianrubisch

This comment has been minimized.

Copy link

@julianrubisch julianrubisch commented May 6, 2021

cool! does navigator.history handle popstate for you, too?

@Intrepidd

This comment has been minimized.

Copy link
Owner Author

@Intrepidd Intrepidd commented May 6, 2021

@julianrubisch

This comment has been minimized.

Copy link

@julianrubisch julianrubisch commented May 6, 2021

awesome, thanks!

It's just a shame that stuff is documented so poorly :-(

@MikeDevresse

This comment has been minimized.

Copy link

@MikeDevresse MikeDevresse commented May 10, 2021

Is there a way to get title in order to push it too ?

@Intrepidd

This comment has been minimized.

Copy link
Owner Author

@Intrepidd Intrepidd commented May 10, 2021

Is there a way to get title in order to push it too ?

I don't think so, the frame update would not include the title.

However you can probably have a stimulus controller that updates the page title on connect given a value, and push a HTML tag in the frame response that would instantiate such controller with a desired title value

@manunamz

This comment has been minimized.

Copy link

@manunamz manunamz commented Jun 1, 2021

For anyone else trying to use stimulus-use without a build system, add StimulusUse before useMutation() and import navigator directly from cdn (but if there's a better way, please comment!). In the end the result will look like this (in javascript):

// js
import { navigator } from 'https://cdn.skypack.dev/@hotwired/turbo';

export default class extends Stimulus.Controller {
  // from: https://gist.github.com/Intrepidd/ac68cb7dfd17d422374807efb6bf2f42
  connect () {
    StimulusUse.useMutation(this, { attributes: true })
  }

  mutate (entries) {
    entries.forEach((mutation) => {
      if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
        const src = this.element.getAttribute('src')
        if (src != null) { navigator.history.push(new URL(src)) }
      }
    })
  }
}

(Stimulus.Controller is also used as stimulus is being imported via <script src="https://unpkg.com/stimulus/dist/stimulus.umd.js"></script>)

@julianrubisch

This comment has been minimized.

Copy link

@julianrubisch julianrubisch commented Jun 1, 2021

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