add scrollTop and scrollLeft bindings - fixes #3780

gh-3780
Richard Harris 5 years ago
parent ad2aac509d
commit e535aed54a

@ -640,7 +640,7 @@ export default class Element extends Node {
message: `'contenteditable' attribute cannot be dynamic if element uses two-way binding`
});
}
} else if (name !== 'this') {
} else if (name !== 'this' && name !== 'scrollTop' && name !== 'scrollLeft') {
component.error(binding, {
code: `invalid-binding`,
message: `'${binding.name}' is not a valid binding`

@ -149,6 +149,10 @@ export default class BindingWrapper {
if (parent.node.get_static_attribute_value('type') === 'file') {
update_dom = null;
}
case 'scrollTop':
case 'scrollLeft':
update_dom = null;
}
if (update_dom) {
@ -170,7 +174,7 @@ export default class BindingWrapper {
if (${this.snippet} !== void 0) {
${update_dom}
}`);
} else if (!/(currentTime|paused)/.test(this.node.name)) {
} else if (update_dom && !/(currentTime|paused)/.test(this.node.name)) {
block.chunks.mount.push(update_dom);
}
}

@ -57,6 +57,12 @@ const events = [
dimensions.test(name)
},
{
event_names: ['scroll'],
filter: (_node: Element, name: string) =>
name === 'scrollLeft' || name === 'scrollTop'
},
// media events
{
event_names: ['timeupdate'],
@ -535,6 +541,32 @@ export default class ElementWrapper extends Wrapper {
b`${resize_listener}.cancel();`
);
} else {
if (name === 'scroll') {
// TODO some duplication between this and Window. needs a comprehensive refactor though
const condition = changed(group.bindings.map(g => g.object));
const scrolling = block.get_unique_name('scrolling');
const scrolling_timeout = block.get_unique_name('scrolling_timeout');
const clear_scrolling = block.get_unique_name('clear_scrolling');
block.add_variable(scrolling, x`false`);
block.add_variable(scrolling_timeout);
block.add_variable(clear_scrolling, x`() => ${scrolling} = false`);
block.chunks.init.push(b`
@add_render_callback(() => ${callee}.call(${this.var}));
`);
block.chunks.update.push(b`
if (${condition} && !${scrolling}) {
${scrolling} = true;
@_clearTimeout(${scrolling_timeout});
${group.bindings.map(binding => b`
${this.var}.${binding.node.name} = ${binding.snippet};`)}
${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100);
}
`);
}
block.event_listeners.push(
x`@listen(${this.var}, "${name}", ${callee})`
);

@ -0,0 +1,72 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
add_render_callback,
attr,
detach,
element,
init,
insert,
listen,
noop,
safe_not_equal
} from "svelte/internal";
function create_fragment(ctx) {
let div1;
let scrolling = false;
let scrolling_timeout;
let clear_scrolling = () => scrolling = false;
let dispose;
add_render_callback(ctx.div1_scroll_handler);
return {
c() {
div1 = element("div");
div1.innerHTML = `<div class="content"></div>`;
attr(div1, "class", "viewport");
dispose = listen(div1, "scroll", ctx.div1_scroll_handler);
},
m(target, anchor) {
insert(target, div1, anchor);
},
p(changed, ctx) {
if ((changed.x || changed.y) && !scrolling) {
scrolling = true;
clearTimeout(scrolling_timeout);
div1.scrollLeft = ctx.x;
div1.scrollTop = ctx.y;
scrolling_timeout = setTimeout(clear_scrolling, 100);
}
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(div1);
dispose();
}
};
}
function instance($$self, $$props, $$invalidate) {
let x;
let y;
function div1_scroll_handler() {
x = this.scrollLeft;
y = this.scrollTop;
$$invalidate("x", x);
$$invalidate("y", y);
}
return { x, y, div1_scroll_handler };
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,10 @@
<script>
let x;
let y;
</script>
<div class="viewport" bind:scrollLeft={x} bind:scrollTop={y}>
<div class="content">
<!-- content goes here -->
</div>
</div>
Loading…
Cancel
Save