S
SolidJS15mo ago
Levi B.

How to handle auto scrolling in a chat app when new message comes in?

So this is an example what i currently have
const App = () => {
const [messages, setMessages] = createSignal([]);
let div;

//Div auto scrolls when new message comes in
createEffect(
on(messages, () => {
div.scrollTo(0, div.scrollHeight);
})
);

somechatprovider.on("new", (msg) => {
setMessages([...messages(), msg])
})

return (
<div ref={container}>
<For each={messages()}>
{(message) => (
<article class={message.author}>
<span>{message.text}</span>
</article>
)}
</For>
</div>
);
};
const App = () => {
const [messages, setMessages] = createSignal([]);
let div;

//Div auto scrolls when new message comes in
createEffect(
on(messages, () => {
div.scrollTo(0, div.scrollHeight);
})
);

somechatprovider.on("new", (msg) => {
setMessages([...messages(), msg])
})

return (
<div ref={container}>
<For each={messages()}>
{(message) => (
<article class={message.author}>
<span>{message.text}</span>
</article>
)}
</For>
</div>
);
};
Now this works but I want to make it such that when the user has scrolled up to read previous messages it doesn't auto scroll. I can't quite figure out how to go about that in solid.
2 Replies
Levi B.
Levi B.15mo ago
OK I've come up with something, just don't know if it's the best
const App = () => {
const [messages, setMessages] = createSignal([]);
let div;

let autoscroll = true;
createRenderEffect(
on(comments, () => {
autoscroll = div.offsetHeight + div.scrollTop > div.scrollHeight - 100;
})
);

createEffect(
on(comments, () => {
if (autoscroll) div.scrollTo(0, div.scrollHeight);
})
);

somechatprovider.on("new", (msg) => {
setMessages([...messages(), msg])
})

return (
<div ref={container}>
<For each={messages()}>
{(message) => (
<article class={message.author}>
<span>{message.text}</span>
</article>
)}
</For>
</div>
);
};
const App = () => {
const [messages, setMessages] = createSignal([]);
let div;

let autoscroll = true;
createRenderEffect(
on(comments, () => {
autoscroll = div.offsetHeight + div.scrollTop > div.scrollHeight - 100;
})
);

createEffect(
on(comments, () => {
if (autoscroll) div.scrollTo(0, div.scrollHeight);
})
);

somechatprovider.on("new", (msg) => {
setMessages([...messages(), msg])
})

return (
<div ref={container}>
<For each={messages()}>
{(message) => (
<article class={message.author}>
<span>{message.text}</span>
</article>
)}
</For>
</div>
);
};
It works but it doesn't feel right, Is there a better way to handle this?
Max
Max15mo ago
Yea its a couple design decisions in here, One thing I would do is just keep the user interaction separate, so either state to represent whether or not the user is scrolling or the amount they did.. scroll delta etc.. then with whatever other information you can still et the actual scroll height As for the computed scroll the way you have it is fine, or can use something like https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor. Both ways combined with tracking the user interaction should be a bit cleaner