Notes on the JupyterLab Notebook HTML DOM Model, Part 2: Code Cells

For a review of markdown cell DOM structure, see here.

Code Cell Structure

The .ipynb JSON format for a markdown cell is defined as follows:

  "cell_type" : "code",
  "execution_count": 1, # integer or null
  "metadata" : {
      "collapsed" : True, # whether the output of the cell is collapsed
      "autoscroll": False, # any of true, false or "auto"
      "tags": []
  "source" : ["some code"],
  "outputs": [{
      # list of output dicts (described below)
      "output_type": "stream",

When rendered as an (empty) code cell, the following HTML DOM structure is evident:

<div class="lm-Widget p-Widget jp-Cell jp-CodeCell jp-mod-noOutputs jp-Notebook-cell jp-mod-active jp-mod-selected">
    <div class="lm-Widget p-Widget jp-CellHeader jp-Cell-header"></div>
    <div class="lm-Widget p-Widget lm-Panel p-Panel jp-Cell-inputWrapper">
        <div class="lm-Widget p-Widget jp-Collapser jp-InputCollapser jp-Cell-inputCollapser">
            <div class="jp-Collapser-child"></div>
        <div class="lm-Widget p-Widget jp-InputArea jp-Cell-inputArea">
            <div class="lm-Widget p-Widget jp-InputPrompt jp-InputArea-prompt">[ ]:</div>
            <div class="lm-Widget p-Widget jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
                <div class="CodeMirror cm-s-jupyter">
                    <div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 5px; left: 4px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div>
                    <div class="CodeMirror-vscrollbar" tabindex="-1" cm-not-content="true">
                        <div style="min-width: 1px; height: 0px;"></div>
                    <div class="CodeMirror-hscrollbar" tabindex="-1" cm-not-content="true">
                        <div style="height: 100%; min-height: 1px; width: 0px;"></div>
                    <div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div>
                    <div class="CodeMirror-gutter-filler" cm-not-content="true"></div>
                    <div class="CodeMirror-scroll" tabindex="-1">
                        <div class="CodeMirror-sizer" style="margin-left: 0px; min-width: 7px; margin-bottom: -15px; border-right-width: 35px; min-height: 27px; padding-right: 0px; padding-bottom: 0px;">
                            <div style="position: relative; top: 0px;">
                                <div class="CodeMirror-lines" role="presentation">
                                    <div role="presentation" style="position: relative; outline: none;">
                                        <div class="CodeMirror-measure"></div>
                                        <div class="CodeMirror-measure"></div>
                                        <div style="position: relative; z-index: 1;"></div>
                                        <div class="CodeMirror-cursors">
                                            <div class="CodeMirror-cursor" style="left: 4px; top: 0px; height: 17px;">&nbsp;</div>
                                        <div class="CodeMirror-code" role="presentation"><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
                        <div style="position: absolute; height: 35px; width: 1px; border-bottom: 0px solid transparent; top: 27px;"></div>
                        <div class="CodeMirror-gutters" style="display: none; height: 62px;"></div>
    <div class="lm-Widget p-Widget jp-CellResizeHandle"></div>
    <div class="lm-Widget p-Widget lm-Panel p-Panel jp-Cell-outputWrapper">
        <div class="lm-Widget p-Widget jp-Collapser jp-OutputCollapser jp-Cell-outputCollapser">
            <div class="jp-Collapser-child"></div>
        <div class="lm-Widget p-Widget jp-OutputArea jp-Cell-outputArea"></div>
    <div class="lm-Widget p-Widget jp-CellFooter jp-Cell-footer"></div>

As in the previous post, the colouring of the block elements corresponds to a particular CSS element scope:

Let’s start at the top, with the <div class="lm-Widget p-Widget jp-Cell jp-CodeCell jp-mod-noOutputs jp-Notebook-cell jp-mod-active jp-mod-selected"> element which covers everything. As with the mardown cell, note the padded area around the cell.

Inside the block are five child elements, including a header and a footer, as with the markdown cell. But in this case, there are two panel elements, (a Cell-inputWrapper and a Cell-outputwrapper), separated by a CellResizeHandle element.

To ground this review in a little more detail, let’s consider the following cell:

The first panel element, the Cell-inputWrapper, covers the whole of the “input” area, which is to say, the whole extent of the block that contains the cell source.

The block contains two elements, an InputCollapser and a the InputArea:

The InputCollapser follows the model used in the markdown cell. One of its most notable features is that it denotes the selected cell. The InputCollapser area is also the one targeted by the nbsafety extension, which provides warning status indications around cells that have been run out of order.

The InputArea cell only ever has two child elements, the InputPrompt interstitial area between the InputCollapser and the Editor, and the Editor itself.

Not how the InputPrompt covers the cell run history count indicator:

The InputPrompt is the one we should target if we were to try to port something like the valuable classic notebook cell execution status extension, which provides a visual indication of cell run statuses (executing, awaiting-exectution, executed-success, executed-error).

The Cell-outputWrapper is the area that contains the cell outputs. Note the upper padding defined on the cell block.

The Cell_outputWrapper contains two elements, the by now familiar Collapser and an OutputArea:

The OutputArea covers the various sorts of output, “printed” output and code cell returned output:

The OutputArea is thus composed of two OutputArea-child elements, only the second of which carries an element distinguishing class, OutputArea-executeResult:

The first child, for printed display output, covers the area to the right of the collapser:

As such, it includes an OutputPrompt and a RenderedText area:

The RenderedText area extends to the right of the gutter area; note the padded area at the start of the element.

The OutputArea-output then contains the printed output content, the styling of which extends the right.

The second OutputArea-child, which is to say, the OutputArea-executeResult element also contains an OutputArea-prompt and a RenderedText area:

The OutputPrompt area includes the cell run history count indicator for the output:

The RenderedText/OutputArea-output element contains the rendered output returned from the executed code.

In the next post in this series, we will look at how we can set classes on the DOM from a JupyterLab extension.

Author: Tony Hirst

I'm a Senior Lecturer at The Open University, with an interest in #opendata policy and practice, as well as general web tinkering...

%d bloggers like this: