Skip to content

Instantly share code, notes, and snippets.

@ebidel ebidel/index.html
Created Feb 15, 2018

Embed
What would you like to do?
<responsive-element> custom element for enabling container/element queries
<!-- based on https://twitter.com/ebidel/status/933496242272747520 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><responsive-element></title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<responsive-container id="container1">
<div>move the &lt;-- frame around or click-me</div>
</responsive-container>
<responsive-container small="400px" id="container2">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
<li>six</li>
<li>seven</li>
<li>eight</li>
<li>nine</li>
<li>ten</li>
</ul>
</responsive-container>
<div id="container3">
<div>right nav</div>
</div>
<script>
const ResizeObservableElement = (superclass) => class extends superclass {
static get observer() {
if (!this._observer) {
// Set up a single RO for all elements that inherit from this class. This
// has much better performance than creating a separate RO in every
// element instance. See https://goo.gl/5uLKZN.
this._observer = new ResizeObserver(entries => {
for (const entry of entries) {
// Custom event works for both node.onresize and node.addEventListener('resize') cases.
const evt = new CustomEvent('resize', {detail: entry, bubbles: false})
entry.target.dispatchEvent(evt);
}
});
}
return this._observer;
}
constructor() {
super();
this.constructor.observer.observe(this);
}
};
class ResponsiveContainer extends ResizeObservableElement(HTMLElement) {
static get is() { return 'responsive-container'; }
get mode() { return this.getAttribute('mode'); }
set mode(val) {
val ? this.setAttribute('mode', val) : this.removeAttribute('mode');
}
constructor() {
super();
this.smallSize = parseInt(this.getAttribute('small')) || 400;
// Component responds to it's own resizes.
this.addEventListener('resize', e => {
const w = e.detail.contentRect.width;
this.mode = w <= this.smallSize ? 'small' : 'large';
});
}
}
customElements.define(ResponsiveContainer.is, ResponsiveContainer);
// Main page can subscribe to component's resize updates too.
document.querySelector('#container2').addEventListener('resize', e => {
console.log(e.detail.contentRect.width, e.detail.contentRect.height);
});
// Just a tester to see how component responds.
const c = document.querySelector('#container1');
c.addEventListener('click', e => {
c.classList.add('off');
setTimeout(() => c.classList.remove('off'), 2000);
});
</script>
</body>
</html>
* {
box-sizing: border-box;
}
html, body {
height: 100vh;
margin: 0;
}
body {
display: flex;
font-family: sans-serif;
color: #455A64;
background: #fff;
}
ul {
list-style: none;
padding: 0;
width: 100%;
}
li {
background: #ffff;
padding: 16px;
margin: 8px;
}
#container1, #container2, #container3 {
display: flex;
align-items: center;
justify-content: center;
}
reponsive-container {
display: block;
}
#container1 {
display: flex;
height: 100vh;
width: 300px;
background: #eee;
will-change: width;
transition: width 600ms ease-in-out;
}
#container1.off {
width: 0;
}
#container2 {
background: #ffcc00;
position: relative;
flex: 1;
overflow: auto;
min-width: 100px;
}
#container2::before {
font-weight: bold;
text-transform: uppercase;
background: blue;
padding: 8px;
position: absolute;
content: attr(mode);
top: 0;
right: 0;
transform: rotateZ(45deg) translate(65px, -35px);
width: 200px;
text-align: center;
color: white;
}
#container2[mode="large"] ul {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#container2[mode="large"] li {
width: 200px;
height: 200px;
}
#container3 {
background: #ccc;
min-width: 100px;
}
@ebidel

This comment has been minimized.

Copy link
Owner Author

commented Feb 15, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.