Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronization of DOM changes from Lisp code and inline <script>s #378

Open
khinsen opened this issue Aug 12, 2024 · 2 comments
Open

Synchronization of DOM changes from Lisp code and inline <script>s #378

khinsen opened this issue Aug 12, 2024 · 2 comments

Comments

@khinsen
Copy link
Contributor

khinsen commented Aug 12, 2024

When I replace the content of an element via clog:inner-html, any <script> elements with inline code in the supplied HTML are executed, but the detailed rules for this execution are not clear. A quick glance at the html() function in JQuery, which is what clog:inner-html ends up calling, only shows that the rules are likely to be complicated.

If such embedded scripts change the DOM, it is important to wait for the end of their execution before inspecting or manipulation the DOM from CLOG. I tried to do this by adding a <script> element at the end of the HTML code that I injected via clog:inner-html. This script does $(clog['document']).trigger('on-load-script','~A'), where ~A gets replaced by a unique token (a gensym). Then I add a handler for the on-load-script event, which uses a semaphore to signal the end of execution to the Lisp level, using the unique token to distinguish between possibly multiple events.

This mechanism frequently fails, in that the semaphore signal arrives before the scripts embedded in the HTML have terminated. As a consequence, my Lisp code working on the DOM sees some intermediate state.

@khinsen
Copy link
Contributor Author

khinsen commented Aug 12, 2024

Original discussion at #377

@khinsen
Copy link
Contributor Author

khinsen commented Sep 5, 2024

I did some more testing today and concluded that this has nothing to do with setting inner-html, nor with <script> elements. I get the same problem doing

(clog:js-execute obj "callFunctionThatTakesAWhileAndCreatesElementsInTheDOM()")
(let ((html-id id-of-an-element-that-was-created-by-that-JS-function))
  (clog:js-execute obj (format nil "clog['~A']=$('#~A').get(0)" html-id html-id)))

In my real use case, the second js-execute happens inside clog:attach-as-child. The function that takes a while calls Graphviz to render a graph to SVG.

What happens is that the query by html-id fails, returning undefined. If I insert (sleep 1) between the two calls, it succeeds.

Next, I tried replacing the first js-execute by blocking-js-execute as defined below. That makes no difference, the lookup still fails.

(defun blocking-js-execute (clog-obj js-code &key (wait-timeout 3))
  (let* ((sem (bordeaux-threads:make-semaphore))
         (document (clog:html-document (clog:connection-body clog-obj)))
         (token (symbol-name (gensym)))
         (code-with-trigger
           (format nil "~A;$(clog['document']).trigger('on-load-script','~A')"
                   js-code token)))
    (flet ((on-load (obj received-token)
             (declare (ignore obj))
             (when (equalp token received-token)
               (bordeaux-threads:signal-semaphore sem))))
      (clog:set-on-load-script document #'on-load :one-time t)
      (clog:js-execute clog-obj code-with-trigger)
      (clog:flush-connection-cache clog-obj)
      (bordeaux-threads:wait-on-semaphore sem :timeout wait-timeout))))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant