Core Concepts
This page explains how NanoJSON models a JSON document as a live, editable DOM tree, and documents the type system's actual behavior — including its edge cases.
JSONEditor: the Root Controller
JSONEditor (src/model/JSONEditor.js) is the sole public entry point. On construction it:
- Injects a stylesheet based on
config.css(loadCSS) - Creates a
Lifecycleinstance wired to thewhen.*callbacks - Loads the initial data via
getJSON()(json/file/path) - Recursively converts the loaded data into a
JSONEditorNodearray via#jsonToChildren() - Mounts to the existing element referenced by
config.id, or creates a new<section class="pd-json-editor">
If, after loading, children.length < 1, it automatically calls insert() once to add an empty node (JSONEditor.js:153-155).
JSONEditorNode: the Recursive Node
JSONEditorNode (src/model/JSONEditorNode.js) represents a single key/value pair or array item, holding key / type / value / children / collapsed. Each node's DOM is rendered locally by #create() and cached in the private field #dom; adding (#add) or removing (#remove) a child only replaces the affected subtree instead of re-rendering the whole tree.
Type System and Edge Cases
Types are derived at runtime by getType() (src/data.js) based on the JavaScript value:
| JS Value | getType() Returns |
|---|---|
Array.isArray(v) is true |
"array" |
typeof v === "object" (including null) |
"object" |
typeof v === "boolean" |
"boolean" |
typeof v === "number" |
"number" |
| anything else | "string" |
getType(null) returns "object", because typeof null === "object" in JavaScript. Whether a null value actually renders as a null-typed node depends on how the caller handles that return value — and the two call sites handle it inconsistently:
- Object child values (
JSONEditor.js:227-231): checksvalue != nullbefore treating it as a nested object/array; ifvalue === null, it explicitly overrides the node's type to"null". - Array child items (
JSONEditor.js:205-209): has no correspondingnullhandling. If an array element isnull,getType()returns"object", but sincee != nullis false, it never enters the nested branch — it falls through to theelsebranch and is stored viaString(e)(the text"null") as a node whose type stays the default"string", not"null".
In practice: importing {"a": null} renders a node whose type dropdown shows null; importing [null] renders a string node containing the literal text "null". If you need array elements to preserve a true null type, you must handle this discrepancy at the application layer.
Serialization: json Getter vs. export()
JSONEditorNode.json (a getter) recursively calls the private #json(), returning the corresponding JavaScript value per node type (array returns an array, object returns an object, boolean/number are coerced, everything else returns a string).
Root-level serialization has two paths, and they do not behave identically:
| Path | Filter Condition (JSONEditor.js) |
Behavior |
|---|---|---|
editor.json (getter, lines 267-277) |
Only includes children where e.key is truthy |
Keyless children (e.g. when the root is an array, or a single scalar) are dropped |
editor.export() (lines 289-310) |
Includes children where e.key is truthy, or when children.length === 1 |
A single keyless root child (array root, scalar root) is still written to the exported file |
In other words: if the root is currently an array (type === "array") with exactly one keyless child, editor.json returns "{}", but editor.export() still downloads the correct data. Be aware of this discrepancy before relying on editor.json when the root type might be an array.
Read-Only Mode
readonly is not a flag propagated to every node individually — after the initial render, it's applied by running document.querySelectorAll(selectorSubNodes) (button:not(.collapsed), textarea, input, select) over the whole already-rendered DOM tree and batch-toggling the disabled attribute (see enable() / disable(), JSONEditor.js:171-183). Toggling read-only does not trigger any Lifecycle hook.
Further Reading
- How updates fire and when hooks run: Lifecycle Hooks
- Full constructor config and method signatures: API Reference