You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Froala Editor中实现手风琴式多级下拉菜单?

在Froala Editor中实现手风琴式多级下拉菜单

我在项目中使用Froala Editor,已经通过编辑器API成功实现了自定义简单下拉菜单,但现在需要创建手风琴式多级下拉菜单,让每个顶级选项可以拥有独立的子选项。

当前简单自定义下拉菜单代码

FroalaEditor.DefineIcon('my_dropdown', { NAME: 'cog', SVG_KEY: 'cogs' });
FroalaEditor.RegisterCommand('my_dropdown', {
  title: 'Advanced options',
  type: 'dropdown',
  focus: false,
  undo: false,
  refreshAfterCallback: true,
  options: {
    'v1': 'Option 1',
    'v2': 'Option 2'
  },
  callback: function (cmd, val) {
    console.log(val);
  },
  refresh: function ($btn) {
    console.log('do refresh');
  },
  refreshOnShow: function ($btn, $dropdown) {
    console.log('do refresh when show');
  }
});

new FroalaEditor('div#froala-editor', {
  toolbarButtons: ['bold', 'italic', 'formatBlock', 'my_dropdown']
});

期望的多级菜单结构

Level 1 - Option 1
Level 1 - Option 2
    Level 2 - Option 1
    Level 2 - Option 2
Level 1 - Option 3

我的尝试代码

我尝试修改refreshOnShow函数,添加多级下拉的嵌套HTML并添加事件处理程序来管理子选项的显示与隐藏,以下是我的尝试代码:

FroalaEditor.DefineIcon('my_multilevel_dropdown', { NAME: 'list', SVG_KEY: 'list' });

FroalaEditor.RegisterCommand('my_multilevel_dropdown', {
  title: 'Advanced options',
  type: 'dropdown',
  focus: false,
  undo: false,
  refreshAfterCallback: true,
  options: {
    'level1_option1': 'Level 1 - Option 1',
    'level1_option2': 'Level 1 - Option 2',
    'level1_option3': 'Level 1 - Option 3'
  },
  callback: function (cmd, val) {
    console.log('Selected:', val);
  },
  refresh: function ($btn) {
    console.log('do refresh');
  },
  refreshOnShow: function ($btn, $dropdown) {
    console.log('do refresh when show');

    // Add custom HTML for multi-level dropdown
    $dropdown.html(`
      <div class="fr-dropdown-wrapper">
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option1">Level 1 - Option 1</div>
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option2">Level 1 - Option 2</div>
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option3">Level 1 - Option 3
          <div class="fr-submenu">
            <div class="fr-command" data-cmd="level2_option1">Level 2 - Option 1</div>
            <div class="fr-command" data-cmd="level2_option2">Level 2 - Option 2</div>
          </div>
        </div>
      </div>
    `);

    // Debug: Check if elements are found
    var $commands = $dropdown.find('.fr-command');
    console.log($commands.length + ' commands found.');

    // Add accordion behavior
    $commands.off('click').on('click', function (event) {
      var $this = $(this);
      var $submenu = $this.children('.fr-submenu');

      if ($submenu.length) {
        event.stopPropagation();
        $submenu.slideToggle();
        $this.siblings().find('.fr-submenu').slideUp();
      } else {
        var cmd = $this.data('cmd');
        $this.closest('.fr-dropdown-wrapper').trigger('click.command', [cmd]);
      }
    });
  }
});

new FroalaEditor('#froala-editor', {
  toolbarButtons: ['bold', 'italic', 'formatBlock', 'my_multilevel_dropdown']
});

修正后的可运行实现

你的思路方向正确,但需要调整几个关键点确保功能正常:

// 定义图标
FroalaEditor.DefineIcon('my_multilevel_dropdown', { NAME: 'list', SVG_KEY: 'list' });

// 注册多级下拉命令
FroalaEditor.RegisterCommand('my_multilevel_dropdown', {
  title: '高级选项',
  type: 'dropdown',
  focus: false,
  undo: false,
  refreshAfterCallback: true,
  // 保留顶级选项用于Froala内部逻辑
  options: {
    'level1_option1': 'Level 1 - Option 1',
    'level1_option2': 'Level 1 - Option 2',
    'level1_option3': 'Level 1 - Option 3'
  },
  callback: function (cmd, val) {
    console.log('选中值:', val);
    // 在这里添加你的业务逻辑
    if (val === 'level2_option1') {
      // 处理二级选项1逻辑
    } else if (val === 'level2_option2') {
      // 处理二级选项2逻辑
    }
  },
  refreshOnShow: function ($btn, $dropdown) {
    // 自定义多级下拉HTML结构
    $dropdown.html(`
      <div class="fr-dropdown-wrapper">
        <div class="fr-command fr-dropdown-item" data-val="level1_option1">Level 1 - Option 1</div>
        <div class="fr-command fr-dropdown-item" data-val="level1_option2">Level 1 - Option 2</div>
        <div class="fr-command fr-dropdown-item has-submenu" data-val="level1_option3">
          Level 1 - Option 3
          <div class="fr-submenu">
            <div class="fr-command fr-dropdown-item" data-val="level2_option1">Level 2 - Option 1</div>
            <div class="fr-command fr-dropdown-item" data-val="level2_option2">Level 2 - Option 2</div>
          </div>
        </div>
      </div>
    `);

    // 添加基础样式(可迁移到外部CSS文件)
    if (!$('.fr-submenu-style').length) {
      $('head').append(`
        <style class="fr-submenu-style">
          .fr-submenu {
            display: none;
            margin-left: 20px;
            margin-top: 5px;
          }
          .has-submenu {
            position: relative;
            padding-right: 25px;
          }
          .has-submenu::after {
            content: '▶';
            position: absolute;
            right: 10px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 12px;
            transition: transform 0.2s;
          }
          .has-submenu.open::after {
            transform: translateY(-50%) rotate(90deg);
          }
        </style>
      `);
    }

    // 绑定点击事件
    $dropdown.find('.fr-command').off('click').on('click', (event) => {
      const $target = $(event.currentTarget);
      const val = $target.data('val');
      const $submenu = $target.find('.fr-submenu');

      if ($submenu.length) {
        // 切换子菜单显示/隐藏
        event.stopPropagation();
        $submenu.slideToggle(200);
        $target.toggleClass('open');
        // 关闭其他子菜单
        $target.siblings('.has-submenu').removeClass('open').find('.fr-submenu').slideUp(200);
      } else {
        // 触发命令回调并关闭下拉菜单
        this.execute('my_multilevel_dropdown', val);
        $dropdown.closest('.fr-dropdown').removeClass('fr-active');
      }
    });
  }
});

// 初始化编辑器
new FroalaEditor('#froala-editor', {
  toolbarButtons: ['bold', 'italic', 'formatBlock', 'my_multilevel_dropdown']
});

关键改进说明

  1. 使用data-val替代data-cmd:避免与Froala内部命令冲突,明确传递选中值
  2. 直接调用this.execute()触发回调:确保Froala命令生命周期正常执行,包括refreshAfterCallback配置
  3. 添加自定义样式:实现子菜单默认隐藏、缩进显示,以及展开/收起的箭头提示
  4. 优化事件逻辑:处理子菜单的手风琴效果,点击子选项后自动关闭下拉菜单

内容的提问来源于stack exchange,提问作者ASB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:27:34