dwmkerr / crosswords-js

Tiny, lightweight crossword control for the web.
https://dwmkerr.github.io/crosswords-js/
MIT License
71 stars 27 forks source link

Be server-side-rendering friendly #39

Open mmkal opened 9 months ago

mmkal commented 9 months ago

Hi @dwmkerr and/or @pvspain - curious if this has come up so far. I'm using crosswords-js on a react application, and have noticed there can be a lag between page load and the crossword being rendered. I think that part of this is the cost of iterating through the clues, validating them, and calling document.createElement(...) for the grid, for each cell, and for each clue.

It's very useful to have the signature of the CrosswordController class just be a model and two elements for the grid and the clues, for getting started quickly. But it'd be useful to be able to pass fully-hydrated HTML element references in, and just have the controller attach the various event handlers/callbacks. That way, the crossword itself could be server-side rendered, cached, and loaded instantly, with the support for interactions coming after the user loads the page.

It might be possible to implement as simply as moving all the document.createElement(...) calls to a method, and call it conditionally, based on whether the dom element looks to be ready already or not:

class CrosswordController {
  constructor(crosswordModel, domGridParentElement, domCluesParentElement) {
    const domElementsReady = this.checkIfDomElementsAreReady(domGridParentElement, domCluesParentElement)
    if (!domElementsReady) {
      this.createView()
    }
  }

  createView() {
    this.#crosswordGridView = this.#document.createElement('div');
    // create cells, clues elements, add classes etc. etc.
  }
}

Then it'd allow people using this library to do things like generate the HTML once when a puzzle is uploaded to a dashboard, and serve that HTML much faster to users when they use it - the structure will look the same for all users.

pvspain commented 9 months ago

Hi @.***,

Great work on the grid resizing mods! I'd been thinking about that for a while, wondering if it was even possible. I'd come across CSS variables, but thought about them as just local variables in the CSS world. I'd seen how you can set styles from Javascript, but setting the variable values from Javascript was the Eureka moment - nicely done!

I've added a 13x13 test crossword sample (data/bletchley_001.json). I've added a file picker to the dev demo and swapping to and from the new puzzle works a treat.

As for speeding up the load time, I've experimented with moving the construction out of the DOMContentLoaded listener in dev/index.js, and it all works ok, with a notable reduction in load time.

As the construction all happens client-side, the only way to improve load time I can see is through concurrency. The Controller and Views have a dependency on the Model, but the Views could be built concurrently in Web Worker https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers threads. The overhead of managing the threads may not exceed the concurrency savings in this case?

I've got a big PR I'll be submitting in the next few days.

The only remaining item on @.*** TODO list is the accessibility features. As I understand, we must have tab indexes https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex set up. Once we tab into the Grid, we can't tab out again with the default keybindings. I think we should keep TAB for navigating between clues as tabbing is the standard navigation action. We need a keybinding to focus the next tabindex after/before the Grid to jump out of the grid.

There may be prior art on this - needs some research!

I'd like to ditch Webpack completely, as it has a caravan of dependencies and is very slow compared to Vite.

The demo site is still broken - can you resolve that please @.***?

Cheers, Paul.

On Tue, 26 Sept 2023 at 03:48, Misha Kaletsky @.***> wrote:

Hi @dwmkerr https://github.com/dwmkerr and/or @pvspain https://github.com/pvspain - curious if this has come up so far. I'm using crosswords-js on a react application, and have noticed there can be a lag between page load and the crossword being rendered. I think that part of this is the cost of iterating through the clues, validating them, and calling document.createElement(...) for the grid, for each cell, and for each clue.

It's very useful to have the signature of the CrosswordController class just be a model and two elements for the grid and the clues, for getting started quickly. But it'd be useful to be able to pass fully-hydrated HTML element references in, and just have the controller attach the various event handlers/callbacks. That way, the crossword itself could be server-side rendered, cached, and loaded instantly, with the support for interactions coming after the user loads the page.

It might be possible to implement as simply as moving all the document.createElement(...) calls to a method, and call it conditionally, based on whether the dom element looks to be ready already or not:

class CrosswordController { constructor(crosswordModel, domGridParentElement, domCluesParentElement) { }

createView() { }}

— Reply to this email directly, view it on GitHub https://github.com/dwmkerr/crosswords-js/issues/39, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFO4S4UVKITRA4V3ZEU4Z3X4G7VXANCNFSM6AAAAAA5GO3FMA . You are receiving this because you were mentioned.Message ID: @.***>