Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/compile/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ export default class Element extends Node {
} else if (contenteditable && !contenteditable.is_static) {
return component.error(contenteditable, compiler_errors.dynamic_contenteditable_attribute);
}
} else if (name !== 'this') {
} else if (name !== 'this' && name !== 'scrollTop' && name !== 'scrollLeft') {
return component.error(binding, compiler_errors.invalid_binding(binding.name));
}
});
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/compile/render_dom/wrappers/Element/Binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ export default class BindingWrapper {
update_dom = null;
mount_dom = null;
}

case 'scrollTop':
case 'scrollLeft':
update_dom = null;
}

if (update_dom) {
Expand Down
31 changes: 31 additions & 0 deletions src/compiler/compile/render_dom/wrappers/Element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ const events = [
filter: (_node: Element, name: string) =>
regex_dimensions.test(name)
},
{
event_names: ['scroll'],
filter: (_node: Element, name: string) =>
name === 'scrollLeft' || name === 'scrollTop'
},
// media events
{
event_names: ['timeupdate'],
Expand Down Expand Up @@ -749,6 +754,32 @@ export default class ElementWrapper extends Wrapper {
b`${resize_listener}();`
);
} 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})`
);
Expand Down
72 changes: 72 additions & 0 deletions test/js/samples/element-binding-scroll/expected.js
Original file line number Diff line number Diff line change
@@ -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;
10 changes: 10 additions & 0 deletions test/js/samples/element-binding-scroll/input.svelte
Original file line number Diff line number Diff line change
@@ -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>