Ah, I didn't realize that "culling" of non-viewport-visible divs happened within your vDOM implementation. I agree that that would need to happen either way. If the "culling" process was a separate layer, though, it could also be implemented on top of the DOM-queuing process I mentioned.
I did this myself when writing a chat client's infinite history scrollback. Chat messages that aren't on the screen still have a pure-data "event" representation of them loaded, but their DOM representation (virtual or otherwise) is entirely discarded. Rather than the physical DOM nodes for chat-message divs having fixed positions such that they're able to disappear without affecting their peers' layout, I instead just have the chat messages stacked as regular block elements under reflow, with two fixed-size "scroll pad" divs just above and below the viewport "consuming" and "releasing" the vertical height that had been taken up by message divs as they're added and removed. You can have millions of rows "loaded" (in the sense of parsed data sitting in JS memory) for instant access, with not much browser memory usage at all.
Though, even that memory usage was too much (this chat client is supposed to run beside memory-intensive apps like games), so as well, in my setup, nodes that are far enough off the screen can have their parsed "event" representation discarded, and replaced with a single URL representing the serialized "history chunk" that those events came from. When the scroll distance comes close to them, the chunks are reloaded by re-requesting and re-parsing the chunk (which, helpfully, is usually still in the browser cache.)
I did this myself when writing a chat client's infinite history scrollback. Chat messages that aren't on the screen still have a pure-data "event" representation of them loaded, but their DOM representation (virtual or otherwise) is entirely discarded. Rather than the physical DOM nodes for chat-message divs having fixed positions such that they're able to disappear without affecting their peers' layout, I instead just have the chat messages stacked as regular block elements under reflow, with two fixed-size "scroll pad" divs just above and below the viewport "consuming" and "releasing" the vertical height that had been taken up by message divs as they're added and removed. You can have millions of rows "loaded" (in the sense of parsed data sitting in JS memory) for instant access, with not much browser memory usage at all.
Though, even that memory usage was too much (this chat client is supposed to run beside memory-intensive apps like games), so as well, in my setup, nodes that are far enough off the screen can have their parsed "event" representation discarded, and replaced with a single URL representing the serialized "history chunk" that those events came from. When the scroll distance comes close to them, the chunks are reloaded by re-requesting and re-parsing the chunk (which, helpfully, is usually still in the browser cache.)