从 Thickbox 到原生 <dialog>:WPJAM Basic 已经实现了 WordPress 弹窗全面升级

熟悉 WordPress 后台开发的都知道,长期以来后台弹窗默认依赖 Thickbox 实现。

我之前也专门整理过文章,讲解 Thickbox 在 WordPress 中的用法与适配实践:在 WordPress 后台使用 ThickBox 制作弹出窗的教程。

但随着现代浏览器标准完善,原生 HTML <dialog> 标签已经全面普及,用来做弹窗比 Thickbox 更简洁、原生、无依赖、体验更好。

所以 WPJAM Basic 新版已经正式弃用 Thickbox,全面切换为原生 <dialog> 弹窗

为什么放弃 Thickbox

Thickbox 是 jQuery 时代的老牌弹窗插件,由 Cody Lindley 开发,核心版本冻结在 3.1(2007 年),2009 年 9 月 30 日正式停止维护,至今已超过 15 年无更新。官网已明确标注 “不再维护,建议使用替代方案”。尽管长期被 WordPress 后台默认集成,但它已彻底成为 “遗留组件”:依赖老旧 jQuery 1.x、无响应式适配、缺少无障碍支持、在现代浏览器与移动端存在兼容性缺陷,且无法适配现代布局与交互标准。

<dialog> 是浏览器原生支持的标准标签,语义化、原生自带弹窗逻辑。

目前主流现代浏览器全都完整支持 <dialog>,除了可怜的 IE,不过我们是在 WordPress 后台后使用,运行环境完全可以覆盖,也无需兼容老旧低端浏览器。

相比 Thickbox 需要写复杂的 CSS 和 JS 来实现垂直和水平居中,原生 <dialog> 标签完全不用额外写任何样式,浏览器默认就自动垂直水平居中,省去大量样式适配和定位调试的麻烦,并且还原生自带遮罩层。

而且 <dialog> 自定义自由度更高,可以随意定制弹窗外观、标题栏、关闭按钮和内容区域,轻松适配 WordPress 后台设计风格;

同时开发更简单、代码更轻量化,原生自带遮罩层、模态锁定、ESC 关闭等能力,不用像 Thickbox 那样手动处理层级冲突、滚动穿透、定位偏移等各种兼容问题,几行代码就能实现标准弹窗效果。

比如我们要实现一个简单的弹窗,首先只需要简单的 HTML 代码:

<dialog id="my-dialog">
    <p>弹窗内容</p>
    <button class="close-dialog">关闭</button>
</dialog>

然后就可以通过 JS 代码实现打开和关闭

// 打开弹窗
$('#my-dialog')[0].showModal();

// 关闭弹窗
$('#my-dialog')[0].close();

汇总在一起,下面这个例子就是通过页面上的按钮打开弹窗了,然后弹窗中也有按钮可以关闭它。

<button class="open-dialog-btn">打开弹窗</button>

<dialog id="my-dialog" style="padding:20px;">
  <h3>原生 Dialog</h3>
  <p>WordPress 后台推荐使用!</p>
  <button class="close-dialog">关闭</button>
</dialog>

<script>
jQuery(function($){
    // 打开
    $('.open-dialog-btn').on('click', function(){
        $('#my-dialog')[0].showModal();
    });

    // 关闭
    $('.close-dialog').on('click', function(){
        $('#my-dialog')[0].close();
    });
});
</script>

此外还可以动态创建弹窗,以及监控关闭等事件,后面我在 WPJAM Basic 插件中的实现会详细展示如何动态创建,以及如何使用和自定义事件。

WPJAM Basic 封装的 dialog 方法

为了方便统一调用,WPJAM Basic 基于原生 <dialog> 封装了 wpjam.dialog() 方法,可以一键实现动态实现弹窗,以及事件监控:

wpjam.dialog = function(...args){
	let id	= 'wpjam_dialog';

	// 无参数:返回当前打开的弹窗实例
	if(!args.length){
		return $('#'+id+'[open]');
	}

	// 获取弹窗 DOM
	let $dialog	= $('#'+id);

	// 如果是 close 命令:关闭弹窗
	if(args[0] === 'close'){
		return $dialog[0]?.close(args[1]);
	}

	// 获取弹窗配置参数
	let data	= args[0];

	// 弹窗不存在:创建弹窗结构并插入页面
	if(!$dialog[0]){
		$dialog	= $('<dialog id="'+id+'" class="wpjam-dialog"><div class="title"><h2></h2><button type="button" commandfor="'+id+'" command="close"></button></div><div class="content"></div></dialog>').appendTo('body');

		// 弹窗关闭事件
		$dialog.on('close', ()=> {
			// 触发关闭自定义事件
			$dialog.trigger('wpjam:dialog:closed');
		}).on('click', 'button[command="close"]', ()=> $dialog[0].close());
	}

	// 设置弹窗宽度、标题、内容
	$dialog.css('width', (data.width || 700)+'px')
		.find('h2').text(data.page_title || '').end()
		.find('.content').html(data.form || data.data || '');

	// 原生方法:打开模态弹窗
	$dialog[0].showModal();

	// 触发打开自定义事件
	$dialog.trigger('wpjam:dialog:opened');

	return $dialog;
};

然后可以通过下面的代码进行调用:

// 打开弹窗
wpjam.dialog({
    page_title: '弹窗标题',
    data: '这里是弹窗内容',
    width: 700
});

// 关闭弹窗
wpjam.dialog('close');

比如 WordPress 后台文章列表页面快速编辑的弹窗:

遇到的小问题,媒体库被遮挡

原以为这样就搞定了,很快就遇到了一个小问题:在弹窗里打开 WordPress 媒体上传器,会被挡在后面

究其原因,原生 <dialog> 自带顶层图层隔离,后续弹出的媒体框无法穿透层级,被压在后面。

解决这个问题,也非常简单:

打开 WordPress 媒体选择弹窗时,先临时关闭当前 <dialog>

wpjam.dialog('close', 'temp');

媒体弹窗选择完成、关闭之后,再重新唤起 <dialog>

wpjam.dialog('show', '');

大概的代码是:

wp.media(args).on('open', function(){
	wpjam.dialog('close', 'temp');

	// 其他媒体库初始化代码
}).on('select', function(){
	// 选择媒体相关代码
}).on('close', function(){
	wpjam.dialog('show', '');
}).open();

然后增强一下 wpjam.dialog 相关代码,实现临时关闭,和显示的功能:

// WPJAM 原生 Dialog 封装方法
wpjam.dialog = function(...args){
	let id	= 'wpjam_dialog';

	// 无参数调用:返回当前处于打开状态的弹窗实例
	if(!args.length){
		return $('#'+id+'[open]');
	}

	// 获取弹窗 jQuery 对象
	let $dialog	= $('#'+id);

	// 调用方式一:关闭弹窗
	if(args[0] === 'close'){
		// 支持带返回值关闭 / 直接关闭
		return args[1] ? $dialog[0]?.close(args[1]) : $dialog[0]?.close();
	}

	// 调用方式二:显示弹窗
	else if(args[0] === 'show'){
		// 如果弹窗存在且未打开,则重新显示
		if($dialog[0] && !$dialog[0].open){
			$dialog[0].returnValue = '';
			$dialog[0].showModal();
		}
		return;
	}

	// 调用方式三:传入配置,打开弹窗
	let data	= args[0];

	// 弹窗 DOM 不存在,则动态创建弹窗结构
	if(!$dialog[0]){
		$dialog	= $('<dialog id="'+id+'" class="wpjam-dialog"><div class="title"><h2></h2><button type="button" commandfor="'+id+'" command="close"></button></div><div class="content"></div></dialog>').appendTo('body');

		// 监听弹窗关闭事件
		$dialog.on('close', ()=> {
			// 如果是临时关闭(temp),不执行后续逻辑
			if($dialog[0].returnValue == 'temp'){
				return;
			}

			// 触发弹窗关闭完成自定义事件
			$dialog.trigger('wpjam:dialog:closed');
		}).on('click', 'button[command="close"]', ()=> $dialog[0].close());	// 点击关闭按钮,关闭弹窗
	}

	// 设置弹窗宽度、标题、内容
	$dialog.css('width', (data.width || 700)+'px')
		.find('h2').text(data.page_title || '').end()
		.find('.content').html(data.form || data.data || '');

	// 调用浏览器原生 API 打开模态弹窗
	$dialog[0].showModal();

	// 触发弹窗打开自定义事件
	$dialog.trigger('wpjam:dialog:opened');

	return $dialog;
};

用「临时关闭 + 事后恢复」的方式,完美解决层级遮挡冲突,优雅又省事。

简单总结一下

  1. WordPress 传统 Thickbox 弹窗老旧笨重,可逐步淘汰;
  2. 原生的 HTML <dialog> 标签 更标准、更轻量、更易用,是后台弹窗的最优替代方案;
  3. WPJAM Basic 已完成全面封装,统一 wpjam.dialog() 调用,结构规范、事件完善;
  4. 遇到媒体弹窗层级遮挡,用「临时 close + 事后 show」即可轻松解决,WPJAM Basic 内置的媒体弹窗已经内置实现。

后续开发 WordPress 后台功能,完全可以直接用原生 <dialog> 替代 Thickbox,开发效率和体验都会提升一大截。


©我爱水煮鱼,本站推荐使用的主机:阿里云,国外主机建议使用BlueHost

本站长期承接 WordPress 优化建站业务,请联系微信:「chenduopapa」。