向IPython Notebook注入/执行JS代码,并在重新加载页面时禁止其进一步执行

Vla*_*mir 5 javascript ipython jupyter-notebook

我正在编写必须将javascript代码嵌入到IPython Notebook中并执行它的库。HTML / JS代码如下所示:

<div id="unique_id"></div>
<script>
var div = document.getElementById("unique_id");
// Do the job and get "output"
div.textContent = output;  // display output after the cell
</script>
Run Code Online (Sandbox Code Playgroud)

和python代码:

from IPython import display
display.display(display.HTML(code))
Run Code Online (Sandbox Code Playgroud)

副作用是javascript代码存储在笔记本中单元格的输出中,并且每次重新加载页面或打开笔记本时,它将再次运行。

有什么方法可以禁止重新加载时执行的代码?还是可以运行javascript代码而不将其保存在输出中?

mxx*_*xxk 7

在遇到在每个打开的笔记本上执行 Javascript 的相同问题后,我将 @Vladimir 的解决方案改编为更通用的形式:

  • 在每次渲染时使用新的唯一 ID(因为旧 ID 与笔记本的 HTML 输出一起保存)。
  • 没有轮询来确定 HTML 元素何时呈现。
  • 当然,当笔记本关闭时,JS所做的HTML修改不会被保存。

关键见解:替换电池输出

from IPython.display import clear_output, display, HTML, Javascript
# JavaScript code here will execute once and will not be saved into the notebook.
display(Javascript('...'))
# `clear_output` replaces the need for `display_id` + `update`
clear_output()
# JavaScript code here *will* be saved into the notebook and executed on every open.
display(HTML('...'))
Run Code Online (Sandbox Code Playgroud)

让它发挥作用

这里的挑战是HTMLJavascript块可能会乱序呈现,并且操作 HTML 元素的代码只需要执行一次。

import random
from IPython.display import display, Javascript, HTML, clear_output

unique_id = str(random.randint(100000, 999999))

display(Javascript(
    '''
    var id = '%(unique_id)s';
    // Make a new global function with a unique name, to prevent collisions with past
    // executions of this cell (since JS state is reused).
    window['render_' + id] = function() {
        // Put data fetching function here.
        $('#' + id).text('Hello at ' + new Date());
    }
    // See if the `HTML` block executed first, and if so trigger the render.
    if ($('#' + id).length) {
        window['render_' + id]();
    }
    ''' % dict(unique_id=unique_id)
    # Use % instead of .format since the latter requires {{ and }} escaping.
))

clear_output()

display(HTML(
    '''
    <div id="%(unique_id)s"></div>
    <!-- When this script block executes, the <div> is ready for data. -->
    <script type="text/javascript">
        var id = '%(unique_id)s';
        // See if the `Javascript` block executed first, and if so trigger the render.
        if (window['render_' + id]) {
            window['render_' + id]();
        }
    </script>
    ''' % {'unique_id': unique_id}
))
Run Code Online (Sandbox Code Playgroud)

为了保持笔记本干净,我会将此管道代码放入单独的 .py 文件中并从 Jupyter 导入。


Vla*_*mir 6

我已经弄清楚了。

诀窍是使用的update=True参数,该参数IPython.display.display()将用新参数替换输出(请参见此处的示例)。

因此,需要做的是:首先输出执行该工作的javascript,然后等待直到创建具有特定ID的div,然后再将其填充到输出中。display()调用此方法后,我们可以调用display第二次,用div用实际的HTML更新第一个。因此,一旦完成,JavaScript代码将用结果填充它,但是代码本身将不会保存。

这是测试代码:

首先,定义回调函数(看起来,在这里将其显示为HTML("<script> ... </script>")而不是十分重要Javascript(...)):

from IPython.display import display, HTML, Javascript

js_getResults = """<script>
function getResults(data, div_id) {
    var checkExist = setInterval(function() {
       if ($('#' + div_id).length) {
          document.getElementById(div_id).textContent = data;

          clearInterval(checkExist);
       }
    }, 100);    
};
</script>"""

display(HTML(js_getResults))
Run Code Online (Sandbox Code Playgroud)

然后在一个单元格中执行更新技巧:

js_request = '$.get("http://slow.server/", function(data){getResults(data, "unique_id");});'
html_div = '<div id="unique_id">Waiting for response...</div>'

display(Javascript(js_request), display_id='unique_disp_id')
display(HTML(html_div), display_id='unique_disp_id', update=True)
Run Code Online (Sandbox Code Playgroud)

执行的回调之后get(),内容Waiting for response...将被服务器的输出替换。

  • 对如何让 JavaScript 只执行一次的深入了解。`HTML("&lt;script&gt;...&lt;/script&gt;)` 和 `Javascript(...)` 之间的主要区别是隐式的 `window` 对象。从 `Javascript( ...)`,将其分配给 `window`,如下所示:`window.getResults = function(...)`。 (2认同)