diff --git a/src/leaf/map/fullscreen/map/map-lifecycle-listener.ts b/src/leaf/map/fullscreen/map/map-lifecycle-listener.ts
new file mode 100644
index 0000000..f22030c
--- /dev/null
+++ b/src/leaf/map/fullscreen/map/map-lifecycle-listener.ts
@@ -0,0 +1,46 @@
+import { DomEvent, DomUtil, type Map } from 'leaflet'
+
+import { setControlTitle } from '../control/set-control-title'
+import { type UseLink } from '../state/use-link'
+
+export type MapLifecycleListenerOptions = {
+  documentFirstReady: boolean
+  getFullscreenState: () => boolean
+  linkAssign: UseLink['assign']
+  map: Map
+  mapFullscreenClassName: string
+  title: Record<string, string>
+  toggleFullscreenState: () => void
+}
+
+export function mapLifecycleListener({
+  documentFirstReady,
+  getFullscreenState,
+  linkAssign,
+  map,
+  mapFullscreenClassName,
+  title,
+  toggleFullscreenState,
+}: MapLifecycleListenerOptions) {
+  return function handleFullscreenMapLifecycleEvent() {
+    DomEvent[documentFirstReady ? 'on' : 'off'](
+      // @ts-expect-error -- `Document` type is assignable to first DomEvent.on argument
+      document,
+      'fullscreenchange',
+      function handleFullscreenMapChange() {
+        ;(getFullscreenState() ? DomUtil.removeClass : DomUtil.addClass)(
+          map.getContainer(),
+          mapFullscreenClassName,
+        )
+
+        map.invalidateSize()
+        toggleFullscreenState()
+        setControlTitle({
+          fullscreen: getFullscreenState(),
+          linkAssign,
+          title,
+        })
+      },
+    )
+  }
+}