浏览器原理 · 4/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

浏览器兼容性问题

常见浏览器兼容性问题及解决方案

问题

前端开发中常见的浏览器兼容性问题有哪些?如何解决?

解答

CSS 兼容性问题

1. 厂商前缀

/* 不同浏览器需要不同前缀 */
.box {
  -webkit-transform: rotate(45deg); /* Chrome, Safari */
  -moz-transform: rotate(45deg);    /* Firefox */
  -ms-transform: rotate(45deg);     /* IE */
  -o-transform: rotate(45deg);      /* Opera */
  transform: rotate(45deg);         /* 标准写法放最后 */
}

/* 使用 Autoprefixer 自动添加前缀(推荐) */
/* 只需写标准写法,构建时自动补全 */
.box {
  transform: rotate(45deg);
}

2. CSS Reset / Normalize

/* 简单的 CSS Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* 或使用 Normalize.css 保留有用的默认样式 */

3. Flexbox 兼容写法

.container {
  display: -webkit-box;      /* 旧版 Safari */
  display: -webkit-flex;     /* Chrome 21-28 */
  display: -ms-flexbox;      /* IE 10 */
  display: flex;
}

JavaScript 兼容性问题

1. 特性检测

// 检测特性是否存在,而不是检测浏览器
if ('querySelector' in document) {
  document.querySelector('.box');
} else {
  document.getElementById('box');
}

// 检测 localStorage
if (typeof Storage !== 'undefined') {
  localStorage.setItem('key', 'value');
} else {
  // 降级方案:使用 cookie
}

// 检测 fetch
if (window.fetch) {
  fetch('/api/data');
} else {
  // 使用 XMLHttpRequest 或引入 polyfill
}

2. Polyfill 示例

// Array.prototype.includes polyfill
if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement, fromIndex) {
    if (this == null) {
      throw new TypeError('this is null or undefined');
    }
    
    const o = Object(this);
    const len = o.length >>> 0;
    
    if (len === 0) return false;
    
    const n = fromIndex | 0;
    let k = Math.max(n >= 0 ? n : len + n, 0);
    
    while (k < len) {
      if (o[k] === searchElement) return true;
      k++;
    }
    return false;
  };
}

// 使用 core-js 按需引入 polyfill(推荐)
import 'core-js/features/array/includes';
import 'core-js/features/promise';

3. 事件兼容

// 事件绑定兼容
function addEvent(element, type, handler) {
  if (element.addEventListener) {
    // 标准方式
    element.addEventListener(type, handler, false);
  } else if (element.attachEvent) {
    // IE8 及以下
    element.attachEvent('on' + type, handler);
  } else {
    element['on' + type] = handler;
  }
}

// 事件对象兼容
function getEvent(e) {
  return e || window.event;
}

function getTarget(e) {
  return e.target || e.srcElement;
}

function preventDefault(e) {
  if (e.preventDefault) {
    e.preventDefault();
  } else {
    e.returnValue = false; // IE
  }
}

function stopPropagation(e) {
  if (e.stopPropagation) {
    e.stopPropagation();
  } else {
    e.cancelBubble = true; // IE
  }
}

工程化解决方案

Babel 配置

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        browsers: ['> 1%', 'last 2 versions', 'not dead']
      },
      useBuiltIns: 'usage', // 按需引入 polyfill
      corejs: 3
    }]
  ]
};

Browserslist 配置

# .browserslistrc
> 1%
last 2 versions
not dead
not ie <= 10

PostCSS + Autoprefixer

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
};

常见兼容问题速查

问题解决方案
IE 不支持 ES6+Babel 转译 + polyfill
CSS3 属性不生效Autoprefixer 添加前缀
默认样式不一致Normalize.css
图片在 IE 下有边框img { border: 0; }
IE 盒模型差异box-sizing: border-box
移动端 300ms 延迟FastClick 或 touch-action: manipulation

关键点

  • 特性检测优于浏览器检测:检测功能是否存在,而非判断浏览器类型
  • 使用构建工具:Babel 处理 JS,Autoprefixer 处理 CSS 前缀
  • 按需 Polyfill:配置 useBuiltIns: 'usage' 避免打包冗余代码
  • 渐进增强:先保证基础功能,再为现代浏览器添加增强体验
  • 明确目标浏览器:通过 Browserslist 统一配置支持范围