【问题标题】:Access an element by id inside Jupyter notebook _repr_javascript_ method在 Jupyter notebook _repr_javascript_ 方法中通过 id 访问元素
【发布时间】:2019-03-02 21:32:53
【问题描述】:

在 Jupyter 输出单元格中,我无法使用其 id 检索新添加的 html 对象。我该怎么做?

编辑:我已经能够在 Azure 上托管的笔记本中复制相同的行为:

https://notebooks.azure.com/rickteachey/projects/sandbox/html/js_repr_id_access.ipynb

注意:要运行此笔记本,请单击右上角的Clone,然后在您自己的 Azure 项目/笔记本中运行它。

javacript 首先使用 Jupyter API 添加一个新的 html 按钮(即,element.html();在上下文中,element 指的是 Jupyter 输出单元格<div>)。

然后代码尝试使用document.getElementById()访问按钮:

class C:
    def _repr_javascript_(self):
        return f'''
        element.html(`<button id="clearBtn">Clear</button>`)
        var x = document.getElementById("clearBtn")
        alert(x)
        '''

C()

预期行为:警报应显示clearBtn html 按钮对象的字符串化版本。

实际行为:警报显示一个空对象,这意味着脚本无法获取 clearBtn - 即使我在查看源代码时可以在 DOM 中看到它。

我可能错误地使用了 API。如果是这样,我该怎么做?

另一个奇怪的问题:当我在 nbviewer 上查看同一个笔记本时,警报会按预期弹出 clearBtn html 对象。它在我的本地计算机或 Azure 上不会以这种方式运行。我应该将此报告为错误吗?

https://nbviewer.jupyter.org/urls/dl.dropbox.com/s/dwfmnozfn42w0ck/access_by_id_SO_question.ipynb

【问题讨论】:

  • 嗨 Rick,在运行你的 Jupyter 之后,我觉得没什么奇怪的,如果你收到警告说“[object HTMLButtonElement]”没关系,这只是意味着 JS 解释器试图对对象进行字符串化并以这种(不是很有用)语法结束。如果您想了解有关此对象的更多信息,我建议您将警报行更改为 console.log(x) 并打开 dev tool console 以更详细地查看您的对象
  • 如我所见,您分享的在线版本由Online Jupyter engine 运行。我建议你检查一下你在本地运行的 jupyter 版本,并与在线引擎的version 进行比较
  • Rick,调试 javascript 代码并不难。断点方法工作正常,您也可以使用控制台的输出进行调试,但我会让您从this doc 中做出选择
  • 我看到了两个可能的答案。 - 当您执行 javascript 代码时,按钮还不存在(易于检查,在运行 js 代码时尝试查找 DOM 元素) - js 代码仅在 python 中运行并且无法访问 DOM(也许检查 nbviewer python 如何链接到 jupyter 页面并将其与您的设置进行比较)
  • 我希望你在这个棘手的问题上好运......我可能有的另一个建议是尝试捕捉你第一行的返回element.html('&lt;button id="clearBtn"&gt;Clear&lt;/button&gt;'),也许你可以通过这种方式直接访问按钮(如果这是你想要的)

标签: javascript python jupyter-notebook jupyter


【解决方案1】:

更新答案,原文如下:

我现在已经对此进行了测试,并认为我解决了为什么下面的问题。具体来说,当输出单元格生成并插入到页面中时,它按以下顺序发生:

  1. 页面从服务器接收数据。
  2. 解析 HTML,包括 &lt;script&gt; 元素。
  3. 执行脚本,似乎以某种我尚未见过的方式将element 置于其本地范围内。 (这与在 nbviewer 页面中发生的情况不同,其中输出已经呈现,脚本通过使用类似 var element = $('#ad74eb90-4105-4cc9-83e2-37fb7e953a9f'); 的方式获取它,可以在源代码中看到。)
  4. 然后才在文档中插入 HTML 内容。

这意味着在脚本运行时,element 位于一个分离的 DOM 节点内。这也可以通过以下方式进行检查:

alert(element[0].parentNode.parentNode) -> null

alert(element[0].parentNode.outerHTML)->

<div class="output_area">
    <div class="run_this_cell"></div>
    <div class="prompt output_prompt">
        <bdi>Out[28]:</bdi>
    </div>
    <div class="output_subarea output_javascript rendered_html">
        <button id="clearBtn">Clear</button>
    </div>
</div>

换句话说,渲染输出的所有操作或遍历都需要通过element变量(例如$("#clearBtn", element)element.find("#clearBtn")甚至element[0].querySelector("#clearBtn"))。它无法通过document,因为脚本运行时该元素还不是文档的一部分。


原答案:

这只是一个模糊的想法:在这种情况下,全局document 是否可能实际上与element 所在的文档不同?编辑器中可能会出现一些 iframe 内容,这可能解释了为什么它在被 nbviewer 渲染到单个页面后但之前没有工作。 (iframe 中的元素不是父文档的一部分,即使浏览器的 DOM 查看器将它们嵌套起来,就好像它们是一样。)

我建议使用element,您必须先找到刚刚插入其中的按钮,而不是尝试从document 中找到它。 (我不确定element 是什么类型的对象,但应该有办法获取它所引用的DOM 节点,然后使用.querySelector("#clearBtn"),对吧?)

编辑:如果element.html() 行是jQuery 代码,那么element 是一个jQuery 对象,element.find("#clearBtn")[0] 会找到包含的按钮。

(这也可以使用element[0].querySelector("#clearBtn") 来完成。注意.find() 的返回值本身就是一个jQuery 对象,并且取消引用jQuery 对象上的[0] 会返回其中的(第一个)DOM 元素。)

【讨论】:

  • 这听起来是一个非常好的建议。但是,严重缺乏element 对象的文档。我在网上几乎找不到任何东西。我对 javascript 也非常缺乏经验,也不知道如何自省对象并查看它可以做什么。更糟糕的是:今天我什至找不到我用来学习如何使用它的链接......我认为它在 SO 但我不确定。
  • 啊哈!找到documentation for IPython.display.Javascript()。它提到jquery是可用的。也许我应该尝试使用 jquery 而不是 vanilla 浏览器 API 来做到这一点?但是我从来没有学过jquery(sadface)。无论如何,jquery 不只是我正在尝试做的事情的语法糖吗?
  • @RickTeachey 如果有 jQuery,那么 element.html() 似乎实际上是 jQuery 代码(看起来像,但我不想假设)。在这种情况下,您想要的命令可能是 element.find("#clearBtn")
  • 你真是个了不起的天才。我会将您更新的答案标记为正确。
  • 所以-据我所知,香草浏览器API的jquery代码语法糖不是吗?为什么$("#name").click(doClick) 可以工作,而document.getElementById("name").addEventListener('click', doClick) 不行?
【解决方案2】:

我看到了两种方法,通过返回 HTML 或 Javascript:

class C:
    def _repr_javascript_(self):
        alert = "alert('x');"
        return f'''
        element.html(`<button onclick="{alert}" id="clearBtn">Clear</button>`)
        '''
C()

class C:
    def _repr_html_(self):
        return f'''
        <button onclick="x()" id="clearBtn">Clear</button>
       <script>
        {{
        var bt = document.getElementById("clearBtn");
        bt.onclick = function(){{ alert('hi'); }};;
        }}        
        </script>
        '''
C()

【讨论】:

    猜你喜欢
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    • 2018-03-08
    • 1970-01-01
    相关资源
    最近更新 更多