Skip to content

Instantly share code, notes, and snippets.

@strothj
Last active February 6, 2023 18:10
  • Star 34 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save strothj/708afcf4f01dd04de8f49c92e88093c3 to your computer and use it in GitHub Desktop.
ResizeObserver TypeScript definition
Copyright 2020 Jason Strothmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* The **ResizeObserver** interface reports changes to the dimensions of an
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)'s content
* or border box, or the bounding box of an
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
*
* > **Note**: The content box is the box in which content can be placed,
* > meaning the border box minus the padding and border width. The border box
* > encompasses the content, padding, and border. See
* > [The box model](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model)
* > for further explanation.
*
* `ResizeObserver` avoids infinite callback loops and cyclic dependencies that
* are often created when resizing via a callback function. It does this by only
* processing elements deeper in the DOM in subsequent frames. Implementations
* should, if they follow the specification, invoke resize events before paint
* and after layout.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
*/
declare class ResizeObserver {
/**
* The **ResizeObserver** constructor creates a new `ResizeObserver` object,
* which can be used to report changes to the content or border box of an
* `Element` or the bounding box of an `SVGElement`.
*
* @example
* var ResizeObserver = new ResizeObserver(callback)
*
* @param callback
* The function called whenever an observed resize occurs. The function is
* called with two parameters:
* * **entries**
* An array of
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
* objects that can be used to access the new dimensions of the element
* after each change.
* * **observer**
* A reference to the `ResizeObserver` itself, so it will definitely be
* accessible from inside the callback, should you need it. This could be
* used for example to automatically unobserve the observer when a certain
* condition is reached, but you can omit it if you don't need it.
*
* The callback will generally follow a pattern along the lines of:
* ```js
* function(entries, observer) {
* for (let entry of entries) {
* // Do something to each entry
* // and possibly something to the observer itself
* }
* }
* ```
*
* The following snippet is taken from the
* [resize-observer-text.html](https://mdn.github.io/dom-examples/resize-observer/resize-observer-text.html)
* ([see source](https://github.com/mdn/dom-examples/blob/master/resize-observer/resize-observer-text.html))
* example:
* @example
* const resizeObserver = new ResizeObserver(entries => {
* for (let entry of entries) {
* if(entry.contentBoxSize) {
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
* } else {
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
* }
* }
* });
*
* resizeObserver.observe(divElem);
*/
constructor(callback: ResizeObserverCallback);
/**
* The **disconnect()** method of the
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
* interface unobserves all observed
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
* targets.
*/
disconnect: () => void;
/**
* The `observe()` method of the
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
* interface starts observing the specified
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
*
* @example
* resizeObserver.observe(target, options);
*
* @param target
* A reference to an
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
* to be observed.
*
* @param options
* An options object allowing you to set options for the observation.
* Currently this only has one possible option that can be set.
*/
observe: (target: Element, options?: ResizeObserverObserveOptions) => void;
/**
* The **unobserve()** method of the
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
* interface ends the observing of a specified
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
*/
unobserve: (target: Element) => void;
}
interface ResizeObserverObserveOptions {
/**
* Sets which box model the observer will observe changes to. Possible values
* are `content-box` (the default), and `border-box`.
*
* @default "content-box"
*/
box?: "content-box" | "border-box";
}
/**
* The function called whenever an observed resize occurs. The function is
* called with two parameters:
*
* @param entries
* An array of
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
* objects that can be used to access the new dimensions of the element after
* each change.
*
* @param observer
* A reference to the `ResizeObserver` itself, so it will definitely be
* accessible from inside the callback, should you need it. This could be used
* for example to automatically unobserve the observer when a certain condition
* is reached, but you can omit it if you don't need it.
*
* The callback will generally follow a pattern along the lines of:
* @example
* function(entries, observer) {
* for (let entry of entries) {
* // Do something to each entry
* // and possibly something to the observer itself
* }
* }
*
* @example
* const resizeObserver = new ResizeObserver(entries => {
* for (let entry of entries) {
* if(entry.contentBoxSize) {
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
* } else {
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
* }
* }
* });
*
* resizeObserver.observe(divElem);
*/
type ResizeObserverCallback = (
entries: ResizeObserverEntry[],
observer: ResizeObserver,
) => void;
/**
* The **ResizeObserverEntry** interface represents the object passed to the
* [ResizeObserver()](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver)
* constructor's callback function, which allows you to access the new
* dimensions of the
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
* being observed.
*/
interface ResizeObserverEntry {
/**
* An object containing the new border box size of the observed element when
* the callback is run.
*/
readonly borderBoxSize: ResizeObserverEntryBoxSize;
/**
* An object containing the new content box size of the observed element when
* the callback is run.
*/
readonly contentBoxSize: ResizeObserverEntryBoxSize;
/**
* A [DOMRectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly)
* object containing the new size of the observed element when the callback is
* run. Note that this is better supported than the above two properties, but
* it is left over from an earlier implementation of the Resize Observer API,
* is still included in the spec for web compat reasons, and may be deprecated
* in future versions.
*/
// node_modules/typescript/lib/lib.dom.d.ts
readonly contentRect: DOMRectReadOnly;
/**
* A reference to the
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
* being observed.
*/
readonly target: Element;
}
/**
* The **borderBoxSize** read-only property of the
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
* interface returns an object containing the new border box size of the
* observed element when the callback is run.
*/
interface ResizeObserverEntryBoxSize {
/**
* The length of the observed element's border box in the block dimension. For
* boxes with a horizontal
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
* this is the vertical dimension, or height; if the writing-mode is vertical,
* this is the horizontal dimension, or width.
*/
blockSize: number;
/**
* The length of the observed element's border box in the inline dimension.
* For boxes with a horizontal
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
* this is the horizontal dimension, or width; if the writing-mode is
* vertical, this is the vertical dimension, or height.
*/
inlineSize: number;
}
interface Window {
ResizeObserver: typeof ResizeObserver;
}
@gordinmitya
Copy link

line 56: static modifier cannot appear on a type member

@1inus
Copy link

1inus commented Jun 22, 2018

publish as a npm project?

@amitbeck
Copy link

amitbeck commented Jul 30, 2018

@gordinmitya

declare namespace DOMRectReadOnly {
  export function fromRect(other: DOMRectInit | undefined): DOMRectReadOnly;
}
interface DOMRectReadOnly {
  readonly x: number;
  readonly y: number;
  readonly width: number;
  readonly height: number;
  readonly top: number;
  readonly right: number;
  readonly bottom: number;
  readonly left: number;

  toJSON: () => any;
}

Could fix that, using declaration merging.

@fpsqdb
Copy link

fpsqdb commented Sep 19, 2018

Should remove DOMRectReadOnly declaration, it already exist in typescript lib (lib.dom.d.ts)

@geodanchev
Copy link

Cannot find name DOMRectInit. Can you tell me where this interface is declared? Is this some external library? Thank you

@jcubic
Copy link

jcubic commented Feb 15, 2019

It don't work if you'll write new ResizeObserver(this.onResize); you need to use new window.ResizeObserver(this.onResize);

@anyexinglu
Copy link

It don't work if you'll write new ResizeObserver(this.onResize); you need to use new window.ResizeObserver(this.onResize);

same problem: TS2339: Property 'ResizeObserver' does not exist on type 'Window'.

image

@DominicTobias-b1
Copy link

DominicTobias-b1 commented Dec 30, 2019

Thanks, fixes it in the code file but produces TS error in the d.ts one:

class ResizeObserver {
Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier.ts(1046)

declare var ResizeObserver: ResizeObserver
Duplicate identifier 'ResizeObserver'.ts(2300)
Individual declarations in merged declaration 'ResizeObserver' must be all exported or all local.ts(2395)

@Daniel15
Copy link

I'm also seeing "Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier.ts(1046)" when I try to use this.

image

@jackfranklin
Copy link

jackfranklin commented Feb 20, 2020

Hey @strothj,

Thanks for this! I work on the Chromium Devtools team and we'd like to pull this definition into Chromium to use in our work.

It would be great if you could clarify the license around re-using this file; would you consider licensing this file under the MIT or Apache license so we can reuse it in Chromium? We'll point back to this gist and make that clear.

Thanks,

Jack.

@sekoyo
Copy link

sekoyo commented Feb 21, 2020

@jackfranklin FYI the most complete and accurate definitions I've seen are those generated by the https://github.com/juggle/resize-observer polyfill (need to run build)

@strothj
Copy link
Author

strothj commented Feb 21, 2020

I'm also seeing "Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier.ts(1046)" when I try to use this.

image

This was a consequence of my having the option "skipLibCheck": true set in my tsconfig.json. I've included the declare keywork on the class. I believe that resolved the issue.

@strothj
Copy link
Author

strothj commented Feb 21, 2020

Hey @strothj,

Thanks for this! I work on the Chromium Devtools team and we'd like to pull this definition into Chromium to use in our work.

It would be great if you could clarify the license around re-using this file; would you consider licensing this file under the MIT or Apache license so we can reuse it in Chromium? We'll point back to this gist and make that clear.

Thanks,

Jack.

I've attached the MIT license.

@m93a
Copy link

m93a commented Apr 12, 2020

Hey @strothj,
thank you for sharing your work with us! If you, or anyone else, wanted to integrate this into TypeScript itself, they said they're accepting PRs.

@ZeeCoder
Copy link

Brilliant work @strothj, really appreciate it.

@x1unix
Copy link

x1unix commented May 6, 2020

Line 240:

Replace with:

interface Window {
  ResizeObserver: typeof ResizeObserver;
}

@m93a
Copy link

m93a commented May 11, 2020

@x1unix Could you explain why?

@Timmmm
Copy link

Timmmm commented May 15, 2020

I'm pretty sure x1unix is correct. ResizeObserver: ResizeObserver means that window.ResizeObserver is an instance of ResizeObserver, but that isn't the case - it is actually a class (well the constructor function, since classes are really functions).

To put it another way, you should be able to do this:

const a = new window.ResizeObserver(...);

This caused an error for me when I was trying to mock ResizeObserver for a test (jsdom doesn't implement ResizeObserver unfortunately). With the existing gist this code causes an error:

export default class ResizeObserver {
  public constructor(callback: ResizeObserverCallback) {}
  public disconnect() {}
  public observe() {}
  public unobserve(target: Element) {}
}

window.ResizeObserver = ResizeObserver;

The last line says something like ResizeObserver is missing properties disconnect, observer, etc. Did you want to use new ResizeObserver()?, which is obviously wrong. Adding typeof fixes that.

@strothj
Copy link
Author

strothj commented May 15, 2020

Nice catch! I've updated the gist as suggested.

@sttz
Copy link

sttz commented Jun 29, 2020

Note that the spec has been updated:

  • ResizeObserverObserveOptions.box has new option "device-pixel-content-box", corresponding to a new property ResizeObserverEntry.devicePixelContentBoxSize.
  • Properties borderBoxSize and contentBoxSize (as well as the new devicePixelContentBoxSize) on ResizeObserverEntry are now arrays (to extend the spec to multi-fragment elements like multi-column layouts in future updates).

@cdpark0530
Copy link

Thank you so much.
For you guys' information: Put everycode inside declare global {} block. Then you'll see the hint globally.

@SuperPenguin
Copy link

SuperPenguin commented Jul 29, 2020

Updated definitions based on latest draft https://www.w3.org/TR/resize-observer/.

declare class ResizeObserver {
    constructor(callback: ResizeObserverCallback);
    observe: (target: Element, options?: ResizeObserverOptions) => void;
    unobserve: (target: Element) => void;
    disconnect: () => void;
}

type ResizeObserverBoxOptions = "border-box" | "content-box" | "device-pixel-content-box";

interface ResizeObserverOptions {
    box?: ResizeObserverBoxOptions;
}

type ResizeObserverCallback = (entries: ResizeObserverEntry[], observer: ResizeObserver) => void;

declare class ResizeObserverEntry {
    readonly target: Element;
    readonly contentRect: DOMRectReadOnly;
    readonly borderBoxSize: ResizeObserverSize[];
    readonly contentBoxSize: ResizeObserverSize[];
    readonly devicePixelContentBoxSize: ResizeObserverSize[];
}

declare class ResizeObserverSize {
    readonly inlineSize: number;
    readonly blockSize: number;
}

interface Window {
    ResizeObserver: typeof ResizeObserver;
    ResizeObserverEntry: typeof ResizeObserverEntry;
    ResizeObserverSize: typeof ResizeObserverSize;
}

Atm, chrome 84 follows these spec, however latest firefox 79 still follows old spec (bug report) so if you want to maintain compatibility with firefox you need to change ResizeObserverEntry to this.

declare class ResizeObserverEntry {
    readonly target: Element;
    readonly contentRect: DOMRectReadOnly;
    readonly borderBoxSize: ResizeObserverSize[] | ResizeObserverSize;
    readonly contentBoxSize: ResizeObserverSize[] | ResizeObserverSize;
    readonly devicePixelContentBoxSize: ResizeObserverSize[];
}

Example usage:

for (const entry of entries) {
    if (entry.contentBoxSize) {
        // Use contentBoxSize
        if (Array.isArray(entry.contentBoxSize)) {
            // New Spec
        } else {
            // Old Spec
        }
    } else {
        // Use contentRect
    }
}

Edit: I just found out that ResizeObserverEntry and ResizeObserverSize is actually a class so for whatever bizarre reason, this should be valid code. I already updated definition above

const observer = new ResizeObserver((entries) => {
    for (const entry of entries) {
        console.log(entry instanceof ResizeObserverEntry); // true

        if (Array.isArray(entry.contentBoxSize)) {
            console.log(entry.contentBoxSize[0] instanceof ResizeObserverSize); // true
        } else {
            console.log(entry.contentBoxSize instanceof ResizeObserverSize); // true
        }
    }
});

@thasmo
Copy link

thasmo commented Oct 8, 2020

Some properties of ResizeObserverEntry should probably not be arrays:

interface ResizeObserverEntry {
    readonly target: Element;
    readonly contentRect: DOMRectReadOnly;
    readonly borderBoxSize: ResizeObserverSize;
    readonly contentBoxSize: ResizeObserverSize;
    readonly devicePixelContentBoxSize: ResizeObserverSize;
}

@sttz
Copy link

sttz commented Oct 8, 2020

@thasmo
They have been changed to sequences in the spec but not all browsers have implemented the change yet:

The box size properties are exposed as sequences in order to support elements that have multiple fragments, which occur in multi-column scenarios. However the current definitions of content rect and border box do not mention how those boxes are affected by multi-column layout. In this spec, there will only be a single ResizeObserverSize returned in the sequences, which will correspond to the dimensions of the first column. A future version of this spec will extend the returned sequences to contain the per-fragment size information.

@capc0
Copy link

capc0 commented Nov 5, 2020

for people finding this by googling: there is an npm package from the DefinitlyTypes project for this API
https://www.npmjs.com/package/@types/resize-observer-browser

@strothj
Copy link
Author

strothj commented Nov 8, 2020

It looks like there's been some progress getting these into the built in DOM typings:
microsoft/TypeScript-DOM-lib-generator#948

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment