newbie question: how to use 'fetch', 'await', or otherwise avoiding TypeError: Cannot read property ... of null

How do I get around TypeError: Cannot read property ... of null\ when I have a function that supplies data to a cell with a specific ID value using \document.getElementById():?

The table will work on play, and my console log is not returning any errors about promises or the like.

Unless I am mistaken, this question hearkens back to @bgchen’s answer to a similar problem, but I still can’t work my way through it. I’ve tried combinations of fetch and .then, but I can’t seem to work out the syntax… none of the spots where I try to close the fetch statement work. I’ve also tried adding in ‘await’ in a few spots, but these doesn’t help.

The examples work fine in HTML, so I’m assuming this has something to do with how Observable determines cell run order?

I’ve read the discussion in the thread Is there a way to know which cell got an error? and looked at @mootari’s Tracking and Displaying Errors’s notebook, but, frankly, a lot of this is over my head. On @bgchen’s prompting, I also read over the Mozilla documentation on fetch and Using Promises.

…I am sure it’s a simple answer :confused:

Here’s where I am at:

Thanks in advance for your guidance and insights!

The TypeError is thrown because your tables haven’t been attached to the DOM yet when the code containing your getElementById() calls runs.

You can deal with this in several ways:

  • Assign a cell name to each of the tables and reference the DOM element directly:
    matchData_fiddle.querySelector('tbody')
    
  • Better yet, don’t modify DOM elements that are “owned” by other cells. Instead have your helper functions create and return the tables.
1 Like

Cool! Thanks Fabian

I’ll read about Helper Functions to see if I can figure out how to use them – and how this differs from the solution you modeled (which is to reference the DOM element directly?).

Thanks for all your time and insights!

Those helpers are snippets that utilize vanilla JS without the help of external libraries. Since you’re already on Observable you can use the helpers from Observable’s Standard Library (this showcases the second point in my previous answer):

1 Like

Awesome! Thank you so much!

You’re very welcome!

Another side note: There’s rarely ever any need to use Element.innerHTML. You’ll generally want to assign to Element.textContent which will automatically escape the assigned string. This is especially important for data that is not controlled by you.
It’s less of an issue on Observable (because the iframe is sandboxed), but missing this step in your own scripts can lead to XSS vulnerabilities.

tl;dr: If you don’t know wether a string contains HTML markup, don’t use innerHTML or Observable’s html`…` template tag. Instead create the element first, then assign to textContent.

1 Like

Thanks again. A couple of days ago I also came across this MDN discussion of Node.textContent, but didn’t fully get it . Your note here boils it down so much better!

I really appreciate all your time and guidance!

You can safely embed arbitrary text content inside of an html tagged template literal by using an embedded expression and a text node, such as using DOM.text. For example:

html`This is text: ${DOM.text("<marquee>hello</marquee>")}`

You’ll see:

This is text: <marquee>hello</marquee>

Not… something else. :slight_smile:

2 Likes

Thanks Mike! Thanks also for linking to The Observable standard library. :slight_smile: