`How can I extract HTMLElements from slots in vue3?`

When attempting to develop a Layer component, I encountered some challenges. Here is the code:

// Wrapper.vue

<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>

// Test.vue

<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>

// Result in HTML:

<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>

The Wrapper component is designed to set the z-index of HTMLElements in its slot["default"].

Although the mutation observer works fine individually (like picture1 and picture2 in the example), it causes issues when multiple Wrappers are used together (like picture3 in the example where picture3's z-index set by wrapper3 is overridden by wrapper1). This occurs because they share the same parent, resulting in shared children elements.

The problem now shifts to "How to retrieve HTMLElements in slots" once again. Can any Vue.js expert assist me with this issue?

Answer №1

The task can be simplified by utilizing Vue Render Functions efficiently: Optimizing Rendering with Slots

const { createApp, h } = Vue;

const ComponentWrapper = {
  props:  {
    name: { type: String, default: "" },
    depth: { type: Number, default: 0 },
  },
  setup(props, { slots }) {
    let childrenElements = slots.default();
    if (childrenElements) {
      for (let j = 0; j < childrenElements.length; j++) {
        childrenElements[j].props.style = `depth: ${props.depth}`;
      }
    }  
    return () => slots.default()
  }
}

const MainApp = { 
  components:  { ComponentWrapper }
}

const mainApplication = createApp(MainApp)
const vmInstance = mainApplication.mount('#main')
#main { line-height: 1.5; }
[v-hidden] { display: none; }
<div id="main" v-hidden>
   <div class="content">
   Content
    <ComponentWrapper name="component1" :depth="1">
      <img class="image1" />
      <div class="section">
        <ComponentWrapper name="component2" :depth="2">
          <img class="image2" />
        </ComponentWrapper>
      </div>
      <ComponentWrapper name="component3" :depth="3">
        <img class="image3" />
      </ComponentWrapper>
    </ComponentWrapper>
  </div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm encountering a problem with handling errors in Express.js: A critical error has occurred while attempting to execute the _runMicro

Currently, I am utilizing the Blizzard API Battle.Net to fetch information regarding a character's name and the realm they inhabit. In certain cases, a user may request a character that does not exist, prompting Blizzard to return a 404 error response ...

Excessive messages are being sent to the child process from the nodejs/electron stream

When trying to send messages from an Electron frontend to a C++ child process via stdin upon button press, I encountered an issue where multiple identical sends were triggered with each click. What is the recommended approach to prevent redundant messages ...

Executing two Ajax calls in ReactJS with different parameters can improve the efficiency of your

Why does the second Ajax call overwrite the first one, causing the results to be different each time I refresh it? In the first Ajax call, I have set tests: [], testsHistories: [] in the setState function. However, the second Ajax call only sets the stat ...

Why is my index.tsx file not properly exporting? (React + Typescript)

I've developed a basic Context Provider that I'd like to package and distribute via npm. To package my code, I utilized the create-react-library tool. In my project, I've set up an index.tsx file that should serve as the entry point for im ...

Eliminating redundant files in the upload section

Currently, I am using lodash clonedeep for the purpose of uploading files. I managed to write a function that prevents users from uploading identical files. However, I have encountered an issue where if I delete a file after uploading it, it remains in th ...

Trouble fetching data for my controller in AngularJS using UI Router resolve

My attempts to inject a resolve object containing loaded data into my controller are resulting in an Unknown Provider error : Error message: Unknown provider: configServiceProvider <- configService Below is the code I am working with: StateProvider ...

Is it feasible for the Drawer to be a fixed feature that sits atop the content?

I am looking to have a compact drawer positioned on the left side of my screen, similar to the example shown in the Material UI Documentation: https://i.sstatic.net/W21Kd.png However, I would like it to float over the content (like the variant="temporary ...

In my React application, I have integrated p5 sketches that constantly loop and repeat

Currently facing a challenge integrating p5 sketches into my React App. I've tried adjusting the z Index of the toolbar to place the p5 elements underneath, but they end up repeating instead. Check out the repository here (sketches can be found in the ...

Adjust the height of images to be consistent

I'm currently working on creating a grid layout with 4 images in a row using DaisyUI card component. However, I've run into an issue where there is white space at the bottom of some images due to varying image heights. Is there a solution that wo ...

Instructions for creating a po file from a js file using poedit

Utilizing the Gettext.js library for localizing content created from a JS file has been my approach. The current challenge lies in the manual creation and writing of each po file. It is known that php files can be scanned for gettext strings using PoEdit ...

The type 'undefined' cannot be assigned to type 'Element or null'

One of the components I am using looks like this: const data: any[] = [] <Tiers data={data}/> This is how my component is structured: const Tiers = ({ data, }: { data?: any; }) => { console.log('data', data?.length!); ...

Simultaneously activate the top and bottom stacked components by clicking on them

One of my components has two child components, A and B. Component A is set to position: absolute and placed on top of component B like an overlay. Additionally, component A has a higher z-index. Both components have onClick events attached to them. My que ...

Is there a way to use Regex to strip the Authorization header from the logging output

After a recent discovery, I have come to realize that we are inadvertently logging the Authorization headers in our production log drain. Here is an example of what the output looks like: {"response":{"status":"rejected",&quo ...

Using Python's Requests library to authenticate on a website using an AJAX JSON POST request

I'm a beginner in Python and struggling to create the correct code for using Python requests to log in to a website. Here is the form code from the website: <form autocomplete="off" class="js-loginFormModal"> <input type="hidden ...

Proper utilization of react-hook-form in conjunction with TypeScript and material-ui to display error messages efficiently

Currently, I am using a combination of react-hook-form with typescript and material-ui. My goal is to pass the error message as a helperText to the TextField. I attempted to achieve this by utilizing helperText={errors.email?.message} however, TypeScript ...

What is the best way to arrange a GeoJSON features array based on a specific property value?

I need help sorting a GeoJSON file based on a property and then slicing it to keep only the top 5 features. For instance, I want to take this GeoJSON and arrange it in descending order by the incidents property: ... [ -75.1972382872565 ...

What is the best way to implement a late-binding clone method in TypeScript classes?

Creating a simple Cloneable interface for all my data classes in JavaScript is a straightforward task. However, when it comes to typing it properly in TypeScript, things get a bit more complex. Currently, I am putting together a solution like this: class ...

Having difficulty in executing the node app.js script

I am currently learning node.js and encountering an issue when trying to run the app.js file using the command node app.js. The terminal shows no output, neither errors nor any other information. Here is the sequence of steps I have followed: $ brew insta ...

An automated feature that smoothly transitions a large image onto the screen

I came across a plugin, possibly a slideshow plugin, that smoothly slides a large image along the y-axis. It's hard to explain, but imagine the visible image is only 600px by 300px, while the actual image is 600px by 600px. This plugin would scroll t ...

Leveraging the injectable service within the end callback function alongside interactJS

When using interactJS with Angular to enable drag and drop functionality for elements with the 'draggable' class, everything was working smoothly until I encountered an issue with using the injected service of the component in the callback functi ...