Vue.js Element Query Component
Using scoped slots and render functions to create a UI-less component that provides child components with information about their size.
by Adam Wathan
HTML
<link rel="stylesheet" href="https://unpkg.com/tailwindcss@0.4.0/dist/tailwind.min.css">
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script src="https://unpkg.com/element-resize-detector@1.1.12/dist/element-resize-detector.min.js"></script>
<div id="app" class="p-8">
<div class="mx-auto mb-8" style="width: 512px;">
<profile-card></profile-card>
</div>
<div class="mx-auto" style="width: 320px;">
<profile-card></profile-card>
</div>
</div>
Babel + JSX
const erd = elementResizeDetectorMaker({ strategy: "scroll" })
Vue.component('element-query', {
data() {
return {
width: null,
height: null,
}
},
mounted() {
erd.listenTo(this.$el, (el) => {
this.width = el.offsetWidth;
this.height = el.offsetHeight;
})
},
render() {
return this.$scopedSlots.default({
width: this.width,
height: this.height,
})
},
})
Vue.component('profile-card', {
template: `
<element-query>
<div slot-scope="{ width }" class="bg-white shadow-lg rounded-lg overflow-hidden">
<div :class="['px-6 py-4', { 'flex items-center': width >= 512 }]">
<img src="https://avatars2.githubusercontent.com/u/4323180?s=400&u=4962a4441fae9fba5f0f86456c6c506a21ffca4f&v=4" alt=""
:class="['block h-16 rounded-full mx-auto mb-4', { 'h-24 mb-0 mr-4 ml-0': width >= 512 }]"
>
<div :class="['text-center', { 'sm:text-left sm:flex-grow': width >= 512 }]">
<div class="mb-4">
<p class="text-xl leading-tight">Adam Wathan</p>
<p class="text-sm leading-tight text-grey-dark">Developer at NothingWorks Inc.</p>
</div>
<div>
<button class="text-xs font-semibold rounded-full px-4 py-1 leading-normal bg-white border border-purple text-purple hover:bg-purple hover:text-white">Message</button>
</div>
</div>
</div>
</div>
</element-query>
`
})
new Vue({ el: '#app' })