SoFunction
Updated on 2025-04-10

Detailed explanation of the use of errors when using recursive generation of the Element-ui NavMenu submenu

When the submenu of the navigation bar is generated in recursive mode, the menu can be generated normally, but when the mouse hover is used, a loop call (mouseenter) event will occur, resulting in an error in the end

How to deal with it

Note: In version 2.13.2, you only need to set the attributes of the submenu:popper-append-to-body="false" to the submenu and this problem will not occur.

The error message is as follows:

Uncaught RangeError: Maximum call stack size exceeded.
    at (:1)
    at invokeWithErrorHandling (:1863)
    at (:2188)
    at ._wrapper (:7547)
    at (:1)
    at invokeWithErrorHandling (:1863)
    at (:2188)
    at ._wrapper (:7547)
    at (:1)
    at invokeWithErrorHandling (:1863)

Test code

Version:

  • vue: v2.6.11
  • element-ui: 2.13.0
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title></title>
  <!-- Introduce styles -->
  <link rel="stylesheet" href="/element-ui/lib/theme-chalk/" rel="external nofollow" >
 </head>
 <body>

  <div >
   <el-menu mode="horizontal">
    <template v-for="(menu,index) in menus">
     <sub-menu v-if=" && " :key="index" :item="menu"></sub-menu>
     <el-menu-item v-else :index="" :key="index">{{  }}</el-menu-item>
    </template>
   </el-menu>
  </div>

  <script src="/npm/vue/dist/"></script>
  <!-- Introducing component library -->
  <script src="/element-ui/lib/"></script>
  <script type="text/javascript">
   ('sub-menu', {
    props: ['item'],
    template: `
     <el-submenu :index="">
      <template slot="title">
       {{}}
      </template>
      <template v-for="(child,index) in ">
       <sub-menu v-if="" :item="child" :key="index"></sub-menu>
       <el-menu-item v-else :key="index" :index="">
        {{}}
       </el-menu-item>
      </template>
     </el-submenu>
    `
   })

   let vm = new Vue({
    el: '#root',
    data() {
     return {
      menus: [{
       title: 'My workbench',
       path: '2',
       children: [{
         title: 'Option 1',
         path: '2-1'
        },
        {
         title: 'Option 2',
         path: '2-2',
        },
       ],
      },{
       title:'Background Management',
       path:'3'
      }]
     }
    },
    components: {}
   })
  </script>
 </body>
</html>

Error analysis

Observe the navigation bar code and error code generated by recursively:

 handleMouseenter: function(e) {
                    var t = this
                      , i =  > 1 && void 0 !== arguments[1] ? arguments[1] : ;
                    if ("ActiveXObject"in window || "focus" !==  || ) {
                        var n = 
                          , r = ;
                        "click" ===  && "horizontal" ===  || ! && "vertical" ===  || r || (("ElSubmenu", "mouse-enter-child"),
                        clearTimeout(),
                         = setTimeout(function() {
                            (, )
                        }, i),
                         && this.$parent.$(new MouseEvent("mouseenter")));//Report an error code                    }
                },

It is speculated that the elements are repeatedly dispatched and accepted by mouseenter events because of the bubble or sinking of events, which has caused a state similar to a dead loop. Due to time constraints, I did not go into it in depth. I will check the root cause when I have time later (if I remember...)

When the mouse is moved into the menu, the handleMouseenter method is triggered, but because appendToBody is true, the mouse move-in event is dispatched, and then it returns to this method, which creates a dead loop. appendToBody is a computed property, so why is appendToBody true? Look at the code:

{
 name: 'ElSubmenu',
    componentName: 'ElSubmenu',
 props:{
  popperAppendToBody: {
         type: Boolean,
         default: undefined   
        }
    },
    computed:{
  appendToBody() {
        return  === undefined     
          ?               // When the specified popperAppendToBody is not displayed, calculate this value          : ;
      },
      isFirstLevel() {
        let isFirstLevel = true;
        let parent = this.$parent;
        while (parent && parent !== ) {
        
        //Calculate whether the first level menu is currently on.        //It seems that there is no problem, because the code already indicates that the current component name is componentName: 'ElSubmenu', but it was found during debugging that the value of componentName is Undefined, so no matter which level it is, the final result is isFirstLevel = true          if (['ElSubmenu', 'ElMenuItemGroup'].indexOf(parent.$) > -1) {
            isFirstLevel = false;
            break;
          } else {
            parent = parent.$parent;
          }
        }
        return isFirstLevel;
      }
 }
}

As for why vue did not collect this parameter when registering the component, it also needs to look at the source code. After lunch break, I have to continue to use the code... I will analyze it if I have time...

How to deal with it

Add a property to el-submenu:popper-append-to-body="true false" explicitly specify that appendToBody is false

Special apology:

The previous processing method was written incorrectly, and it was written as: popper-append-to-body="true". Therefore, even if this attribute is added, it still reports an error. I apologize here!

This is the article about detailed explanation of the use of errors when using the Element-ui NavMenu submenu to generate the Element-ui NavMenu submenu. This is the end of this article. For more related contents of the Element-ui NavMenu submenu to generate the Element-ui NavMenu submenu, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!