如何在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'] });
关键改进说明
- 使用
data-val替代data-cmd:避免与Froala内部命令冲突,明确传递选中值 - 直接调用
this.execute()触发回调:确保Froala命令生命周期正常执行,包括refreshAfterCallback配置 - 添加自定义样式:实现子菜单默认隐藏、缩进显示,以及展开/收起的箭头提示
- 优化事件逻辑:处理子菜单的手风琴效果,点击子选项后自动关闭下拉菜单
内容的提问来源于stack exchange,提问作者ASB
相关产品推荐
相关产品推荐

