How to get HTMLElements in slot using vue3?
P粉794177659
2023-09-01 17:57:52
<p>当我尝试开发 Layer 组件时发生了一些事情。代码如下:</p>
<p><em>// Wrapper.vue</em></p>
<pre class="brush:php;toolbar:false;"><template>
<slot v-bind="attrs"></slot>
</template>
<script lang="ts" setup>
import {
defineProps,
ref,
watch,
useAttrs,
onMounted,
onUnmounted,
getCurrentInstance,
} from "vue";
const props = defineProps({
name: { type: String, default: "" },
zIndex: { type: Number, default: 0 },
});
const attrs = useAttrs();
const inst = getCurrentInstance();
const observer = ref<MutationObserver | null>(null);
watch(
() => [props.zIndex, props.name],
() => {
setBrothersAttrs();
}
);
onMounted(() => {
let el = inst?.vnode.el;
if (el) {
if (!observer.value)
observer.value = new MutationObserver(setBrothersAttrs);
observer.value.observe(el.parentElement, { childList: true });
}
setBrothersAttrs();
});
onUnmounted(() => {
if (observer.value) observer.value.disconnect();
});
const setBrothersAttrs = () => {
let el = inst?.vnode.el;
let children = el?.parentElement?.children;
if (el && children) {
for (let i = 0; i < children.length; i ) {
let bro = children[i];
if (bro === el) continue;
bro.style.zIndex = props.zIndex;
}
}
};
</script></pre>
<p><em>// Test.vue</em></p>
<pre class="brush:php;toolbar:false;"><template>
<div class="test">
<Wrapper name="wrapper1" :z-index="1">
<img class="picture1" />
<div class="inner">
<Wrapper name="wrapper2" :z-index="2">
<img class="picture2" />
</Wrapper>
</div>
<Wrapper name="wrapper3" :z-index="3">
<img class="picture3" />
</Wrapper>
</Wrapper>
</div>
</template>
<script lang="ts" setup>
import Wrapper from "./Wrapper.vue";
</script></pre>
<p><em>// Results in HTML format: </em></p>
<pre class="brush:php;toolbar:false;"><div class="test">
<img class="picture1" style="z-index: 1" /><!-- if no wrapper3, "z-index: 3" -->
<div class="inner" style="z-index: 1">
<img class="picture2" style="z-index: 2" />
</div>
<img class="picture3" style="z-index: 1" /><!-- if no wrapper3, "z-index: 3" -->
</div></pre>
<blockquote>
<p>As shown in the above code, the component Wrapper is an empty component. Its purpose is to set the z-index of HTMLElements in slot["default"]. </p>
</blockquote>
<p>Unfortunately, I can't find the child element in useSlots()["default"]. <strong>So I tried to have a mutation observer observe the parent's childList. </strong></p>
<p>It works fine on its own (<em>like picture 1 and picture 2 in the example</em>). <strong>But when multiple Wrapper are wrapped together, it will cover each other (<em>Like picture 3 in the example, the z-index of picture3 set by wrapper3 is overwritten by wrapper1). </strong>This means that they share the same parent, so that the parent's children are always the same. </p>
<p>So now the question becomes again "<strong>How to get HTMLElements in slot</strong>"...can any vuejs guru help me? </p>
This can be done more simply using Vue render functions: Rendering slots