mirror of https://github.com/sveltejs/svelte
feat: add back `<svelte:document>` (#7149)
Closes #3310 --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>pull/8387/head
parent
c19d0889c5
commit
4b0b471ee1
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
title: <svelte:body>
|
|
||||||
---
|
|
||||||
|
|
||||||
Similar to `<svelte:window>`, the `<svelte:body>` element allows you to listen for events that fire on `document.body`. This is useful with the `mouseenter` and `mouseleave` events, which don't fire on `window`.
|
|
||||||
|
|
||||||
Add the `mouseenter` and `mouseleave` handlers to the `<svelte:body>` tag:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<svelte:body
|
|
||||||
on:mouseenter={handleMouseenter}
|
|
||||||
on:mouseleave={handleMouseleave}
|
|
||||||
/>
|
|
||||||
```
|
|
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let selection = '';
|
||||||
|
|
||||||
|
const handleSelectionChange = (e) => selection = document.getSelection();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:body />
|
||||||
|
|
||||||
|
<p>Select this text to fire events</p>
|
||||||
|
<p>Selection: {selection}</p>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let selection = '';
|
||||||
|
|
||||||
|
const handleSelectionChange = (e) => selection = document.getSelection();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:document on:selectionchange={handleSelectionChange} />
|
||||||
|
|
||||||
|
<p>Select this text to fire events</p>
|
||||||
|
<p>Selection: {selection}</p>
|
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: <svelte:document>
|
||||||
|
---
|
||||||
|
|
||||||
|
Similar to `<svelte:window>`, the `<svelte:document>` element allows you to listen for events that fire on `document`. This is useful with events like `selectionchange`, which doesn't fire on `window`.
|
||||||
|
|
||||||
|
Add the `selectionchange` handler to the `<svelte:document>` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svelte:document on:selectionchange={handleSelectionChange} />
|
||||||
|
```
|
||||||
|
|
||||||
|
> Avoid `mouseenter` and `mouseleave` handlers on this element, these events are not fired on `document` in all browsers. Use `<svelte:body>` for this instead.
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: <svelte:body>
|
||||||
|
---
|
||||||
|
|
||||||
|
Similar to `<svelte:window>` and `<svelte:document>`, the `<svelte:body>` element allows you to listen for events that fire on `document.body`. This is useful with the `mouseenter` and `mouseleave` events, which don't fire on `window`.
|
||||||
|
|
||||||
|
Add the `mouseenter` and `mouseleave` handlers to the `<svelte:body>` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svelte:body
|
||||||
|
on:mouseenter={handleMouseenter}
|
||||||
|
on:mouseleave={handleMouseleave}
|
||||||
|
/>
|
||||||
|
```
|
@ -0,0 +1,41 @@
|
|||||||
|
import Node from './shared/Node';
|
||||||
|
import EventHandler from './EventHandler';
|
||||||
|
import Action from './Action';
|
||||||
|
import Component from '../Component';
|
||||||
|
import TemplateScope from './shared/TemplateScope';
|
||||||
|
import { Element } from '../../interfaces';
|
||||||
|
import compiler_warnings from '../compiler_warnings';
|
||||||
|
|
||||||
|
export default class Document extends Node {
|
||||||
|
type: 'Document';
|
||||||
|
handlers: EventHandler[] = [];
|
||||||
|
actions: Action[] = [];
|
||||||
|
|
||||||
|
constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) {
|
||||||
|
super(component, parent, scope, info);
|
||||||
|
|
||||||
|
info.attributes.forEach((node) => {
|
||||||
|
if (node.type === 'EventHandler') {
|
||||||
|
this.handlers.push(new EventHandler(component, this, scope, node));
|
||||||
|
} else if (node.type === 'Action') {
|
||||||
|
this.actions.push(new Action(component, this, scope, node));
|
||||||
|
} else {
|
||||||
|
// TODO there shouldn't be anything else here...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private validate() {
|
||||||
|
const handlers_map = new Set();
|
||||||
|
|
||||||
|
this.handlers.forEach(handler => (
|
||||||
|
handlers_map.add(handler.name)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (handlers_map.has('mouseenter') || handlers_map.has('mouseleave')) {
|
||||||
|
this.component.warn(this, compiler_warnings.avoid_mouse_events_on_document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import Block from '../Block';
|
||||||
|
import Wrapper from './shared/Wrapper';
|
||||||
|
import { x } from 'code-red';
|
||||||
|
import Document from '../../nodes/Document';
|
||||||
|
import { Identifier } from 'estree';
|
||||||
|
import EventHandler from './Element/EventHandler';
|
||||||
|
import add_event_handlers from './shared/add_event_handlers';
|
||||||
|
import { TemplateNode } from '../../../interfaces';
|
||||||
|
import Renderer from '../Renderer';
|
||||||
|
import add_actions from './shared/add_actions';
|
||||||
|
|
||||||
|
export default class DocumentWrapper extends Wrapper {
|
||||||
|
node: Document;
|
||||||
|
handlers: EventHandler[];
|
||||||
|
|
||||||
|
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
|
||||||
|
super(renderer, block, parent, node);
|
||||||
|
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
|
||||||
|
add_event_handlers(block, x`@_document`, this.handlers);
|
||||||
|
add_actions(block, x`@_document`, this.node.actions);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
export default {
|
||||||
|
html: '<div></div>',
|
||||||
|
|
||||||
|
async test({ assert, target, window }) {
|
||||||
|
const visibility = new window.Event('visibilitychange');
|
||||||
|
|
||||||
|
await window.document.dispatchEvent(visibility);
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>
|
||||||
|
<div class="tooltip">Perform an Action</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
let container;
|
||||||
|
function tooltip(node, text) {
|
||||||
|
let tooltip = null;
|
||||||
|
|
||||||
|
function onVisibilityChange() {
|
||||||
|
tooltip = document.createElement('div');
|
||||||
|
tooltip.classList.add('tooltip');
|
||||||
|
tooltip.textContent = text;
|
||||||
|
container.appendChild(tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.addEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:document use:tooltip="{'Perform an Action'}" />
|
||||||
|
<div bind:this={container} />
|
Loading…
Reference in new issue