如何用jQuery/JavaScript监听元素display或visibility变化执行操作
监听元素display/visibility变为可见状态的实现方案
你之前写的if($('.class').css('display') == 'none')属于一次性静态取值判断,只能在代码执行的瞬间拿到元素状态,无法持续追踪属性变更,自然实现不了事件触发的效果。由于display和visibility属性变更本身不会触发原生DOM事件,目前生产环境可用的可靠方案有两种,优先推荐无侵入、性能更优的MutationObserver实现。
方案1:MutationObserver实现(推荐,兼容绝大多数现代浏览器)
这个是W3C标准提供的DOM变动监听API,不需要修改你原有控制元素显隐的业务代码,只需要在元素上绑定监听即可,只会在属性发生变动时触发回调,没有空转性能消耗。
/** * 监听元素变为可见状态(display:block/visibility:visible) * @param {string|jQuery} selector 选择器或者jQuery对象 * @param {Function} callback 触发时执行的回调 */ function onShow(selector, callback) { const $targets = selector instanceof jQuery ? selector : $(selector); $targets.each(function() { const el = this; // 缓存初始状态 let lastDisplay = getComputedStyle(el).display; let lastVisibility = getComputedStyle(el).visibility; const observer = new MutationObserver(() => { const curDisplay = getComputedStyle(el).display; const curVisibility = getComputedStyle(el).visibility; // 判断是否从隐藏切到了可见状态 const isShow = (curDisplay !== 'none' && lastDisplay === 'none') || (curVisibility === 'visible' && lastVisibility !== 'visible'); // 更新状态缓存 lastDisplay = curDisplay; lastVisibility = curVisibility; if (isShow) callback.call(el, { display: curDisplay, visibility: curVisibility }); }); // 只监听class和style属性变动,过滤无关属性变更减少性能消耗 observer.observe(el, { attributes: true, attributeFilter: ['class', 'style'], subtree: false }); // 挂载实例到元素上,方便后续取消监听 $(el).data('showObserver', observer); }); } /** * 取消可见状态监听 */ function offShow(selector) { const $targets = selector instanceof jQuery ? selector : $(selector); $targets.each(function() { const observer = $(this).data('showObserver'); if (observer) { observer.disconnect(); $(this).removeData('showObserver'); } }); } // 调用示例 onShow('.your-class', function(status) { console.log('元素已变为可见', this, status); // 此处写需要自动执行的业务逻辑即可,this指向当前触发的DOM元素 });
方案说明
- 不管你是直接修改内联style、切换CSS类控制显隐,都能正常捕获,不需要侵入原有业务代码
- 兼容Chrome 18+、Firefox 14+、Safari 6+、Edge全版本,覆盖99%以上的线上用户场景
- 没有定时轮询的空转消耗,性能最优
方案2:定时轮询兜底(兼容IE10及以下老旧浏览器)
如果你的项目需要兼容不支持MutationObserver的极老旧浏览器,可以用短间隔轮询做兜底,100ms的间隔人眼无感知,性能消耗也在可控范围内。
function onShowPoll(selector, callback, gap = 100) { const $targets = selector instanceof jQuery ? selector : $(selector); $targets.each(function() { const el = this; let lastDisplay = getComputedStyle(el).display; let lastVisibility = getComputedStyle(el).visibility; const timer = setInterval(() => { const curDisplay = getComputedStyle(el).display; const curVisibility = getComputedStyle(el).visibility; const isShow = (curDisplay !== 'none' && lastDisplay === 'none') || (curVisibility === 'visible' && lastVisibility !== 'visible'); lastDisplay = curDisplay; lastVisibility = curVisibility; if (isShow) callback.call(el, { display: curDisplay, visibility: curVisibility }); }, gap); $(el).data('showPollTimer', timer); }); } function offShowPoll(selector) { const $targets = selector instanceof jQuery ? selector : $(selector); $targets.each(function() { const timer = $(this).data('showPollTimer'); if (timer) { clearInterval(timer); $(this).removeData('showPollTimer'); } }); }
方案说明
- 轮询间隔默认设100ms即可,不要设置小于16ms的间隔避免不必要的性能浪费
- 非必要场景不优先使用这个方案
常见踩坑提示
- 不要用jQuery自带的
:visible伪类做状态判断,这个方法会把父元素隐藏的场景也算作元素不可见,如果只需要判断元素本身的display/visibility属性,直接读getComputedStyle的返回值最准确 - 如果元素的显隐受父级元素控制,可以在监听目标上把父节点也加入观察列表,或者根据业务需要调整
subtree配置 - 回调内的
this直接指向触发状态变更的原生DOM元素,需要用jQuery方法时直接包一层$(this)即可
内容的提问来源于stack exchange,提问作者trying2code
相关产品推荐
相关产品推荐

