Typescript and Working with HTML Elements (nulls?)

hello all,

I’m following Sean Bradley’s course on Threejs using Typescript.
I’m very new, but I feel like I’m finally starting to make sense of things.
However, occasionally when following others’ tutorials not using Typescript I sometimes run into problems.

Currently, I am trying to make a loading screen for my application.

const progressBar = document.getElementById('progress-bar') as HTMLInputElement | null

loadingManager.onProgress = function(url, loaded, total) {
    progressBar.value = ((loaded / total) * 100)
    console.log('currently loading: ${url}')
}

const progressBarContainer= document.querySelector('.progress-bar-container') as HTMLElement

loadingManager.onLoad = function() {
    progressBarContainer.style.display = 'none'
    console.log('finished loading')
}

the progressBarContainer successfully disappears after the scene is loaded, but I cannot get this progressBar or its value to make typescript happy.

where “progressBar.value” is listed as a possible null.
I believe I have the progress-bar defined in my HTML document

also, following this tut:
https://youtu.be/zMzuPIiznQ4

Thank you for any help you can offer to a beginner.

Alex

What type of element is your progress bar? .value is only attributed to elements such as inputs or options, if you’re using a div you’ll need to set a style property such as left or another to the value of your progress

Hi forerunrun,

Thanks for your response.
I may be erring on posting too much, but since you mentioned styles, I wanted to make sure y’all had the whole story.

This is my index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <link rel="stylesheet" type="text/css" rel="noopener" target="_blank" href="css.css">
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Teignmouth Electron</title>
        <div id="info">Double Click/Tap to Reset View</div>
        <style>
            body {
                overflow: hidden;
                margin: 0px;
            }
            .progress-bar-container {
                    position: absolute;
                    left: 50%;
                    top: 50%;
                    transform: translate(-50%, -50%);
                    width: 100%;
                    height: 100%;
                    background-color: rgba(0, 0, 0, 0.8);
                    display: flex;
                    flex-direction: column;
                    justify-content: center;
                    align-items: center;
            }

            #progress-bar {
                width: 30%;
                margin-top: 0.5%;
                height: 2%;
            }

            label {
                color: white;
                font-size: 2rem;
                font-family: Arial, Helvetica, sans-serif;
            }
        </style>
    </head>
    <body>
        <div class="progress-bar-container">
            <label for="progress-bar">Loading...</label>
            <progress id="progress-bar" value="0" max="100"></progress>
        </div>
        <script type="module" src="bundle.js"></script>
    </body>
</html>

Thanks again. Hoping to learn from this roadblock.

Alex

document.getElementById('progress-bar') may or may not yield a result that’s why TS is unhappy. this must correctly infer the type:

const progressBar = document.getElementById('progress-bar')

but the the issue is this:

           ↓
progressBar.value

because once again, progressBar might be undefined but you’re reading from it which could crash (if progressBar couldn’t be found), which is what TS tries to protect you from. a simple check should be enough.

if (progressBar) { 
  progressBar.value = ((loaded / total) * 100)

if you know that progressBar cannot be null then you can force TS to just accept that with a ! and now you don’t have to check for its presence:

const progressBar = document.getElementById('progress-bar')!
...
progressBar.value = foo

Hi drcmda,

Thanks for taking the time to break this down and speak to TS’s conditions and needs.
You offer a great explanation, one that gets me thinking about wildcards and casting in an engine like Unreal.
I think I understand why this is happening in the first place, that TS is not in direct conversation with the HTML document and can’t guarantee the existence of the items I’m requesting.

Adding the “!” has satisfyied the call to “progressBar” but not “.value”
similarly, adding the “if (progressBar) {” satisfies progressBar but with the same error for “.value” continuing to claim property does not exist on type HTMLElement, and of course, why should it? It has no way of knowing what’s in the HTML document.

Do I need to offer a similar “!” specific to the .value?

Here’s what I have

const progressBar = document.getElementById('progress-bar')!

loadingManager.onProgress = function(url, loaded, total) {
if (progressBar) {
    progressBar.value = ((loaded / total) * 100)
}}
progressBar.value = foo

I really don’t know if you intended on my including the last line, as I understood it to be your explanation that now progressBar.value could satisfactorily = anything
But its inclusion or exlusion didn’t change anything and I tried putting it in other places.

Like I said, I’m new to coding, more versed on node-based programming like Unreal’s Blueprints or Houdini, so I get into trouble with sequencing and proper inputs.

Thanks again,
Alex

i think it needs to be cast as HTMLProgressElement!

the reason it can’t be certain that the query will return a result is because the dom is mutable. anything could come in between and remove a node.

wow! that did it.
Lots of dialect/vocabulary to learn.

Thanks.