AmazingDayToday
AmazingDayToday

Reputation: 4282

TS2339: Property 'style' does not exist on type 'Element'

Here's the code:

const test = Array.from(document.getElementsByClassName('mat-form-field-infix'));
test.forEach((element) => {
    element.outerHTML = '<div class="good-day-today" style="width: 0px;"></div>'; // Please note that this line works fine!
    element.style.padding = '10px';
    element.style.borderTop = '0';
});

Error I get when compiled:

ERROR in src/app//.component.ts(101,21): error TS2339: Property 'style' does not exist on type 'Element'. src/app//.component.ts(102,21): error TS2339: Property 'style' does not exist on type 'Element'.

How can I fix it?

I tried to remove the Array.from... part, tried to use for of and for in, tried as any, but above is the way I have to do it.

Upvotes: 151

Views: 235834

Answers (8)

Jonathan
Jonathan

Reputation: 904

While I suspect that type casting will not cause any problems in this case, I would still avoid type casting whenever possible. In this case, you can avoid type casting with instanceof narrowing:

const test = Array.from(
  document.getElementsByClassName('mat-form-field-infix')
)
test.forEach((element) => {
  if (!(element instanceof HTMLElement)) {
    throw new TypeError(`Expected a object of Type HTMLElement`)
  }

  element.outerHTML =
    '<div class="good-day-today" style="width: 0px;"></div>' // Please note that this line works fine!
  element.style.padding = '10px'
  element.style.borderTop = '0'
})

Upvotes: 3

Mostafa Mohammadzadeh
Mostafa Mohammadzadeh

Reputation: 911

Maybe this could help:

let element = <HTMLElement> document.getElementsByClassName(className)[0];

Upvotes: 5

Levminer
Levminer

Reputation: 61

I think I found a way easier method:

Just create an index.d.ts file and add:

interface Element {
    style: CSSStyleDeclaration
}

Upvotes: 2

Carlo Stanzione
Carlo Stanzione

Reputation: 59

A workaround could be doing something like this:

element["style"].padding = '10px';
element["style"].borderTop = '0';

Maybe it's not the best solution, but it should work, I used it multiple times :)

Upvotes: 5

rasujan
rasujan

Reputation: 21

I was also facing a similar type of issue while doing

document.querySelectorAll(".<className>"); 

so instead of adding the style property, I simply got around it by just adding another class.

example:

//css file
    .<classname> {
        display: none;
      }
    .<classname>.show {
        display: flex;
      }

//ts file
elements.forEach((ele, index) => {
const errors = something.length;
if (index < errors) {
  ele.classList.add("show");
} else {
  ele.classList.remove("show");
}

}); };

Upvotes: 2

CertainPerformance
CertainPerformance

Reputation: 371233

Another option is to use querySelectorAll and a type parameter. getElementsByClassName is not generic, but querySelectorAll is - you can just pass the type of the elements that will be selected, like this:

const test = document.querySelectorAll<HTMLElement>('.mat-form-field-infix');

This doesn't require any type casting, and it will allow you to use forEach immediately, rather than converting it to an array first. (getElementsByClassName returns an HTMLCollection, which does not have a forEach method; querySelectorAll returns a NodeList, which does have a forEach method, at least on somewhat up-to-date browsers. To support ancient browsers too, you'll need a polyfill, or convert it to an array first.)

If you happen to just need a single element, you can use querySelector, which is generic as well:

const elm = document.querySelector<HTMLElement>('.foo')!;
elm.style.padding = '10px';

Another benefit of querySelectorAll (and querySelector) over the many other options is that they accept CSS selector strings, which can be far more flexible and precise. For example, the selector string

.container > input:checked

will select children of <div class="container"> which are <input>s and are checked.

Upvotes: 167

Andrew
Andrew

Reputation: 776

When you set the outerHTML, you're destroying the original element that was there. So, your styling doesn't work.

You'll notice that if you change it to set innerHTML, your styling does work.

This does not do the same exact thing, but I hope it points you in the right direction.

const test = Array.from(document.getElementsByClassName('mat-form-field-infix'));
test.forEach((element) => {
    element.innerHTML = '<div class="good-day-today" style="width: 0px;"></div>'; // Please note that this line works fine!
    element.style.padding = '10px';
    element.style.borderTop = '0';
});

Upvotes: 2

Jonas Wilms
Jonas Wilms

Reputation: 138557

You need a typecast:

Array.from(document.getElementsByClassName('mat-form-field-infix') as HTMLCollectionOf<HTMLElement>)

That's because getElementsByClassName only returns HTMLCollection<Element>, and Element does not have a styleproperty. The HTMLElement however does implement it via it's ElementCSSInlineStyle extended interface.

Note that this typecast is typesafe in the way that every Elementis either a HTMLElement or an SVGElement, and I hope that your SVG Elements don't have a class.

Upvotes: 175

Related Questions