How can I reproduce the title of on Observable input?

Exporting input cells to HTML documents that have custom CSS definitions (e.g. default Bootsrap CSS), I find input tiles don’t always render out well. I would like to display the title text in another, separate cell and assign it different styling.

Is there some way to return the title text?

For example, if my input were:

viewof energy_transmission_type = textarea({
  title: "Energy Transmission Type", 
  placeholder: "Specifiy type(s) of transmission project.", 
  spellcheck: true,
  width: "100%",
  rows: 1,
  submit: false,

I would like to do something like write energy_transmission_type.title and have it return just the text Energy Transmission Type.

Is there an easy way to do this?

1 Like

Assuming you used, you can access the title with:

viewof energy_transmission_type.querySelectorAll('div')[1].innerText
1 Like

Amazing! Thank you!!

Thanks to you (and to make it easier for me for find later) I published a demo:


1 Like

You can also use:

viewof energy_transmission_type.querySelector('div>div').innerText

Thank you, @mootari!

I remain a bit confused by what is happening both with your code and the given by @severo . Specifically, @severo’s appears to be selecting the first div element contained within the input element–hence that bit: ('div')[1] I noticed that can switch it out with the ('div')[0] and get the same result.

In yours, you appear to be ‘mapping’ div to div, right? Or is > in your code ‘greater than’? If it’s a mapping, does it mean “take all div (innerText) and return as div”?

For what it’s worth, when I extend out the entire element tree from viewof energy_transmission_type, I see a lot of ‘Forbidden’, but not an actual <div> element.


Any insights on how to better understand how this all fits together?

When in doubt, consult the documentation. :wink: Relying on false assumptions can send you down paths that are difficult to return from.

Let’s deconstruct both variants. Our element’s (simplified) HTML looks like this:

    <div>Energy Transmission Type</div>
    <textarea>Upgrading and automation.</textarea>
  • First, a recap: viewof is an ObservableHQ construct that, when used in a cell declaration, displays an HTML element, but passes its value property on to other cells. When we reference another cell via viewof (and that cell has been declared with viewof) we gain access to the DOM element and its API.
  • On to selections:
    • querySelectorAll() receives a CSS selector string and returns all matched elements as a NodeList (which, for the most part, behaves like an array). Because the div we’re looking for comes second, we access it via its index [1].
    • querySelector() acts similar to querySelectorAll(), but returns only the first matched element.
  • Next, the selector string div>div: ">" is the child combinator. It only matches direct children. In this case, any div that is the direct child of another div.
  • Finally, the innerText mystery: .innerText returns an element’s visible text content, but excludes text contained in input elements. It doesn’t matter how deeply nested the text is.
    To observe the difference between selecting the parent div and its child, add a description to your textarea widget, and query the first div by using .querySelectorAll('div')[0] or .querySelector('div').

Wow @mootari! This is exceptionally helpful! I’ll continue to read through the documentation. It helps a lot to have this sort of guidance though, as in the absence of it I find it still very challenging to make sense of things…

Thank you so much! :1st_place_medal:

Still one hold-up I have in following through your comments though:

… this is what I was referring to when I wrote:

… When I enter viewof energy_transmission_type and it returns an HTML form element, even extending out the entire tree I don’t see the <div> elements that make up the input. Instead, I get lots of elements like this (snippet):

HTMLFormElement {
  0: HTMLTextAreaElement {
  <prototype>: HTMLTextAreaElement {
  autocomplete: [forbidden]
  cols: [forbidden]
  dirName: [forbidden]
  disabled: [forbidden]
  form: [forbidden]
  maxLength: [forbidden]
  minLength: [forbidden]
  name: [forbidden]
  placeholder: [forbidden]
  readOnly: [forbidden]
  required: [forbidden]
  rows: [forbidden]
  wrap: [forbidden]
  type: [forbidden]

How did you arrive at that ‘simplified’ HTML structure and know to look for the text in the <div> for the title? And how did we know it ‘comes second’? (And it appears that its also comes first, as I get the same value for ('div')[0] ?

… I will keep reading to see if I can’t figure out more of this myself. Probably I am missing something in all those dozens (or hundreds) of fields being reported for the inputs HTML element…

Thank you so much for your time and guidance!

1 Like

The Observable Inspector is a terrible choice to traverse a DOM tree. :wink:

To view any element’s markup, right-click it and select Inspect:

In the Elements panel of your browser’s Developer Tools you can right-click the element and select Copy > Copy outerHTML:

I copied the outerHTML as described above, then cleaned it up by hand.

By the way, have you ever wondered how Observable decides when to display an element, and when to inspect it?


As it turns out, the Inspector will check the element’s .parentNode property. If the element is already attached to another element the Inspector kicks in.


Alternatively, you can use this inline inspector:

Use an import-with to pass it a node to inspect. Plus, it supports updating views without collapsing thanks to the React library it uses.


I am very grateful for all this help and guidance!

Thank you.

1 Like