How to get the root DOM element of an el-popover component in Element-Plus?

154 views Asked by At

I'm using the el-popover component from Element-Plus in my Vue project to create a popover effect. Here's the code:

<el-button ref="saveButtonRef" class="misc-button" :loading="saveLoading" @click="OnSheetSave">Save</el-button>
<el-popover ref="savePopoverRef" :virtual-ref="saveButtonRef" :visible="showSaveList" placement="top-start" :width="500" virtual-triggering>
   ...el-popover Content...
</el-popover>

I want the popover to close when the mouse clicks outside of both the saveButtonRef and savePopoverRef instances. Therefore, I added an event listener:

document.addEventListener('click', handleClickPopoverOutside);

function handleClickPopoverOutside(event) {
    if (showSaveList.value) {
        const buttonElement = saveButtonRef.value.$el;
        const popoverElement = savePopoverRef.value.$el;
        if (buttonElement && !buttonElement.contains(event.target) && popoverElement && !popoverElement.contains(event.target)) {
            OnSaveSheetPoPoverClose();     // Close Popover Instance
        }
    }
}

However, this doesn't work as expected. When I click on the savePopoverRef instance, it still triggers the OnSaveSheetPoPoverClose() function. I found that the reason is because savePopoverRef.value.$el returns #text. Is there a way to properly get the root DOM element of the el-popover component, or is there another approach to close the popover when clicking outside of it?

As mentioned above, I want to be able to close the popover by triggering my function OnSaveSheetPoPoverClose() when clicking outside of the saveButtonRef and savePopoverRef instances. And I don't want to change the virtual-ref of savePopoverRef from saveButtonRef, as it would affect my other logic.

1

There are 1 answers

0
Raeisi On BEST ANSWER

For hiding a popup programmatically in Vue, I take a reverse approach to handling the events. It is easier and more general.

  1. Listen to click event globally (on the window object) by window.addEventListener
  2. Stop the button and the popup events by @click.stop

This way, I can catch the click events that occur outside of the popup, while letting popup content do and process their click event.

Here is a sample demo:

<link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
<script src="//unpkg.com/vue@3"></script>
<script src="//unpkg.com/element-plus"></script>

<body>
  <div id="app">
    <el-button 
    class="misc-button"
    @click.stop="toggleDialog"
    >
    Show Dialog
    </el-button>
    
    <el-button 
    ref="saveButtonRef"
    class="misc-button"
    :loading="saveLoading"
    @click.stop="OnSheetSave"
    >
    Save
    </el-button>

    <el-popover 
    :visible="showSaveList"
    placement="top-start" 
    :width="300"
    virtual-triggering
    >
      <div style="min-width: 100px; min-height: 100px;border: 1px dashed red;" @click.stop>
        Your pop-up content goes here. Clicking anywhere on the popup won't close the modal, but clicking outside triggers the event handler and closes the modal
      </div>
    </el-popover>

  </div>
  <script>
    const App = {
      data() {
        return {
          showSaveList: false,
        };
      },
      methods: {
        OnSaveSheetPoPoverClose() {
         console.log('OnSaveSheetPoPoverClose got called');
         this.toggleDialog(); // hide dialog and remove the event listener
         this.OnSheetSave();
        },
        
        OnSheetSave(){
          console.log('OnSheetSave got called');
        },
        
        toggleDialog() {
          if (this.showSaveList) {
              this.showSaveList = false;
              window.removeEventListener('click', this.OnSaveSheetPoPoverClose);
          } else {
             this.showSaveList = true;
             window.addEventListener('click', this.OnSaveSheetPoPoverClose);
          }
        },    
      },     
    };
    const app = Vue.createApp(App);
    app.use(ElementPlus);
    app.mount("#app");
        
  </script>
</body>

Explanation

There is a Show Dialog button, which shows the popup and adds the event listener ensuring the OnSaveSheetPoPoverClose gets called on every click.

To prevent common elements from triggering the OnSaveSheetPoPoverClose, I stopped their click event (prevent it from propagating) using @click.stop.

Note

Make sure to remove event listeners at the proper time.