Last active
May 19, 2026 16:39
-
-
Save mrclay/6620bab25c8553ca9be9f14d4b283932 to your computer and use it in GitHub Desktop.
React/web components to measure available inline width
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (() => { | |
| /** | |
| * Measures the available inline width. | |
| * | |
| * This example will show the width of the following P element: | |
| * | |
| * <div class="constrain"> | |
| * <measure-width></measure-width> | |
| * <p>...</p> | |
| * </div> | |
| */ | |
| class MeasureWidthElement extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: "open" }); | |
| this._observer = null; | |
| this._onResize = () => this.update(); | |
| } | |
| connectedCallback() { | |
| this.update(); | |
| window.addEventListener("resize", this._onResize); | |
| this._observer = new ResizeObserver(() => this.update()); | |
| this._observer.observe(this); | |
| } | |
| disconnectedCallback() { | |
| window.removeEventListener("resize", this._onResize); | |
| if (this._observer) { | |
| this._observer.disconnect(); | |
| this._observer = null; | |
| } | |
| } | |
| update() { | |
| const width = this.getBoundingClientRect().width; | |
| const [whole, decimal] = String(Math.round(width * 1000) / 1000).split( | |
| ".", | |
| ); | |
| const rest = decimal ? `.${decimal}` : ""; | |
| const style = ` | |
| :host { | |
| background-color: #fff; | |
| background-image: linear-gradient(to bottom, #ccc, #ccc); | |
| background-size: 100% 1px; | |
| background-position: center; | |
| background-repeat: no-repeat; | |
| border-inline: 1px solid #ccc; | |
| border-block: 5px solid transparent; | |
| color: #000; | |
| display: block; | |
| font: 14px/17px system-ui, sans-serif; | |
| text-align: center; | |
| } | |
| span[a] { | |
| display: inline-block; | |
| background: #fff; | |
| padding: 0 4px; | |
| } | |
| span[b] { | |
| opacity: 0.5; | |
| } | |
| `; | |
| this.shadowRoot.innerHTML = ` | |
| <style>${style}</style> | |
| <span a>${whole}<span b>${rest}</span></span> | |
| `; | |
| } | |
| } | |
| if (!customElements.get("measure-width")) { | |
| customElements.define("measure-width", MeasureWidthElement); | |
| } | |
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useState, useRef, useLayoutEffect } from 'react'; | |
| /** | |
| * Measures the available inline width. | |
| * | |
| * This example will show the width of the following P element: | |
| * | |
| * <div class="constrain"> | |
| * <MeasureWidth /> | |
| * <p>...</p> | |
| * </div> | |
| */ | |
| function MeasureWidth() { | |
| const [width, setWidth] = useState(0); | |
| const rulerRef = useRef<HTMLDivElement>(null); | |
| useLayoutEffect(() => { | |
| const element = rulerRef.current; | |
| if (!element) { | |
| return; | |
| } | |
| const update = () => { | |
| const rect = element.getBoundingClientRect(); | |
| setWidth(rect.width); | |
| }; | |
| // Initial calculation | |
| update(); | |
| const observer = new ResizeObserver(update); | |
| observer.observe(element); | |
| // eslint-disable-next-line consistent-return | |
| return () => { | |
| observer.disconnect(); | |
| }; | |
| }, []); | |
| const widthString = String(Math.round(width * 1000) / 1000); | |
| const [whole, decimal] = widthString.split('.'); | |
| const rest = decimal ? `.${decimal}` : ''; | |
| const styles: Record<string, React.CSSProperties> = { | |
| container: { | |
| backgroundColor: '#fff', | |
| backgroundImage: 'linear-gradient(to bottom, #ccc, #ccc)', | |
| backgroundSize: '100% 1px', | |
| backgroundPosition: 'center', | |
| backgroundRepeat: 'no-repeat', | |
| borderInline: '1px solid #ccc', | |
| borderBlock: '5px solid transparent', | |
| color: '#000', | |
| display: 'block', | |
| font: '14px/17px system-ui, sans-serif', | |
| textAlign: 'center', | |
| width: '100%', | |
| boxSizing: 'border-box', | |
| }, | |
| spanA: { | |
| display: 'inline-block', | |
| background: '#fff', | |
| padding: '0 4px', | |
| }, | |
| spanB: { | |
| opacity: 0.5, | |
| }, | |
| }; | |
| return ( | |
| <div ref={rulerRef} style={styles.container}> | |
| <span style={styles.spanA}> | |
| {whole} | |
| <span style={styles.spanB}>{rest}</span> | |
| </span> | |
| </div> | |
| ); | |
| } | |
| export default MeasureWidth; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment