Open khinsen opened 3 months ago
Original discussion at https://github.com/rabbibotton/clog/discussions/377
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))))
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 thehtml()
function in JQuery, which is whatclog: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 viaclog:inner-html
. This script does$(clog['document']).trigger('on-load-script','~A')
, where ~A gets replaced by a unique token (agensym
). Then I add a handler for theon-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.