8

I'm trying to get the elements by class name from the DOM in typescript. It seems pretty straight forward what I'm doing here but the console shows an error.

function showSlides() {
   var i;
   var slides = <HTMLElement[]<any>document.getElementsByClassName('slide');

   for (i = 0; i < slides.length; i++) {
     slides[i].style.display = "none";
   }
}

The expected result should be an array with 3 items and it should change the display style to none, but the actual result is a JS error: Uncaught TypeError: Cannot read property 'style' of undefined.

Tofetopo
  • 456
  • 2
  • 7
  • 20
  • 1
    This is not a TS error. It's JS. It just means that `slides[]` at a certain index does not have an element (value), therefore it is `undefined` and you can't read a property of "nothing". – Milkncookiez Feb 11 '19 at 12:52
  • So what could be the fix for this? I have tried querySelectorAll, but it just works with elements and not with class names. – Tofetopo Feb 11 '19 at 12:58
  • What happens when you log the value of `slides`? – Mathyn Feb 11 '19 at 12:58
  • I'm not sure why you are using ``. `getElementsByClass` already returns a array of HTML elements. I've created a Plunkr using your code example and I'm not having trouble > http://plnkr.co/edit/f5XKghZz7aEQLKZdz2af?p=preview – Peter Boomsma Feb 11 '19 at 13:04
  • @PeterBoomsma because when I use document.getElementByClassName I have an error in the code: Property 'style' does not exist on type 'Element', I think its a TypesCript thing, and thats why I need to cast it as HtmlElement – Tofetopo Feb 11 '19 at 13:11
  • @Tofetopo ah yes of course. Check this > https://stackoverflow.com/questions/12686927/typescript-casting-htmlelement – Peter Boomsma Feb 11 '19 at 13:16
  • @Tofetopo Are you using Angular or plain JS? – Prashant Pimpale Feb 11 '19 at 13:20
  • I am using Vue, so this code is going into a Vue component. – Tofetopo Feb 11 '19 at 13:34
  • It doesn't matter if you use a JS framework. This is just a simple TypeScript error because you're trying to use a `style` property on a wrong type. Have you checked my answer? – Peter Boomsma Feb 11 '19 at 13:45

3 Answers3

15

document.getElementsByClassName returns a HTMLCollection of Element objects.

These Elements could be one of a few different types, HTMLElement and SVGElement being two common ones.

If you know for sure that all the elements you're looping over are HTMLElements then you can cast to that.

const showSlides = () => {
    const slides = document.getElementsByClassName('slide');

    for (let i = 0; i < slides.length; i++) {
        const slide = slides[i] as HTMLElement;
        slide.style.display = "none";
    }
};

If it's possible you'll get back non-HTMLElement elements, then you'll need to figure out how to test for them.

One way of doing that might be:

const showSlides = () => {
    const slides = document.getElementsByClassName('slide');

    for (let i = 0; i < slides.length; i++) {
        const slide = slides[i];

        if (slide instanceof HTMLElement) {
            // slide is a HTMLElement
            slide.style.display = "none";
        } else if (slide instanceof SVGElement) {
            // slide is a SVGElement
            const svgOwner = slide.ownerSVGElement;
        } else {
            // slide is a Element
            const baseUri = slide.baseURI;
        }
    }
};
3

Took me a bit. But I've wrestled with this as well.

const slides = Array.from(document.getElementsByClassName('slide'));

for (const x of slides) {
    const y = <HTMLElement> x;
    y.style.display = 'none';
}

This should work.

More info on the for of loop > https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html

If you're using TS you should skip the JS way of looping through a Array and use the TypeScript way.

getElementsByClassName returns a array of elements. And you can't use style on a Element. Only on a HTMLElement. So you have to cast the Element into a HTMLElement. If you enclose your getElementsByClassName in a Array.from it returns a Element array. From that point you can loop through the Element array in the for loop. And then you cast each object in that Element array to a HTML element array and then you can use .style on that object.

If you have questions you can hit me up.

Peter Boomsma
  • 8,851
  • 16
  • 93
  • 185
2

The style property exists only on HTMLElement, yet slides is a collection of a broader type: Element.

In order to safely manipulate style, see if an element is, in fact, an HTMLElement first.

function showSlides() {
  var i;
  var slides = document.getElementsByClassName('slide');

  for (i = 0; i < slides.length; i++) {
    const slide = slides[i];

    if (slide instanceof HTMLElement) {
      slide.style.display = "none";
    }
  }
}
Karol Majewski
  • 23,596
  • 8
  • 44
  • 53