Skip to content

兼容性问题

因为不同浏览器使用的内核不同,对同样的代码有不同的解析,造成页面显示效果不统一的效果。

  • IE(Trident 内核)
  • Firefox(火狐:Gecko 内核)
  • Safari(苹果:webkit 内核)
  • Chrome、Opera、Edge(谷歌:Blink 内核)

同一浏览器,版本越老,存在 bug 越多,相对于版本越新的浏览器,对新属性和标签、新特性支持越少。

不同浏览器,核心技术(内核)不同,标准不同,实现方式也有差异,最终呈现出来在大众面前的效果也是会有差异。浏览器兼容性问题可分为以下三类

  • 渲染相关:和 样式 相关的问题,即体现在布局效果上的问题。
  • 脚本相关:和 脚本 相关的问题,包括 JavaScript 和 DOM、BOM 方面的问题。对于某些浏览器的功能方面的特性,也属于这一类。
  • 代码不规范:不按照 W3c 标准写代码,造成的问题。

解决兼容性问题思路

  • 渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

  • 优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

解决方案

  • 选择通用技术框架/库(如 jquery Vue React):再内部已经解决了大部分兼容性问题。
  • 选择兼容工具: html5shiv 、 Respond.js 、 CSS Reset 、 normalize.css 、 Modernizr.js 、 postcss 。
  • 条件注释、 CSS Hack 、 修补 js 能力检测。
  • 用户更新高版本浏览器。

推荐https://caniuse.com/这个查询网站。它是一个针对前端开发人员定制的一个查询CSS、JS、HTML5、SVG在主流浏览器中特性和兼容性的网站,可以很好的保证网页在浏览器中的兼容性。

js 兼容问题

老版本浏览器不支持 ES6 语法。使用 babel(Babel 是一种工具链,主要用于将 ECMAScript 2015+代码转换为当前和旧版浏览器或环境中的向后兼容版本的 JavaScript)。但是 Babel 默认只转换新的 JavaScript 语法(syntax),而不转换新的 API ,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。

IE 兼容性问题

js
// IE8以上使用attachEvent添加事件处理器
function addEvent(elm, evType, fn, useCapture) {
  if (elm.addEventListener) { // W3C标准
    elm.addEventListener(evType, fn, useCapture);
    return true;
  } else if (elm.attachEvent) { // IE
    var r = elm.attachEvent('on' + evType, fn); // IE5+
    return r;
  } else {
    elm['on' + evType] = fn; // DOM事件
  }
}

// 阻止冒泡的兼容
function(event){
  if (event.stopPropagation) {
	event.stopPropagation()
} else {
	event.cancelBubble = true
 }
}

//阻止默认行为的兼容
function(event) {
  // 执行自定义逻辑
  if (event.preventDefault) {
    event.preventDefault();
  } else {
    event.returnValue = false;
  }
  return false;
};

//滚动事件的兼容
scrollTop = document.documentElement.scrollTop || document.body.scrollTop

// IE 只能使用 var 关键字来定义常量,

//E下,event 对象有 x、y 属性,但是没有 pageX、pageY属性
var myX = event.x ? event.x : event.pageX;
var myY = event.y ? event.y : event.pageY;

// 用CSS禁止选取网页内容,在IE需要用JS禁止
user-select: none;
obj.onselectstart = function {return false;} // IE
  • 图片变化 base64 格式之后,再添加查询字符串,会报错
  • 移动端更改同名图片无法清除缓存。所以,还是要在图片命名上做文章

CSS 兼容性问题

  • 使用 normalize.css抹平差异,同时可以定制自己的 reset.css 全局重置样式
  • CSS3 兼容前缀表示:可以使用 Autoprefixer 是一个用于自动添加 CSS 浏览器前缀的 postcss 插件,可以根据 Can I Use 数据库来确定需要添加哪些前缀,以满足特定的浏览器兼容性要求。配置.browserslistrc 文件,定义需要兼容的浏览器。
  • 图片加 a 标签在 IE9 中会有边框:边框设置为 0 img{border:none}
  • IE9 以下浏览器不能使用 opacity
css
opacity: 0.7; /*FF chrome safari opera*/
filter: alpha(opacity: 70); /*用了ie滤镜,可以兼容ie*/
  • 鼠标指针 cursor 兼容问题:统一使用 {cursor:pointer}
  • a 标签 css 状态的顺序:按照 link–visited–hover–active 的顺序编写
  • 在 Chrome 中字体不能小于 12px
css
p {
  font-size: 12px;
  transform: scale(0.8);
}

移动端兼容性问题

IOS

  • 部分设备切换横竖屏时,会缩放字体
css
/* iOS 禁止弹出各种操作窗口 */
-webkit-touch-callout:none

/* 禁止 iOS 和 Android 用户选中文字: */
-webkit-user-select:none

/* 部分手机上,切换横竖屏时,会缩放字体。禁止文字缩放 */
-webkit-text-size-adjust:100%;

/* 可以用来控制字体的像素显示是否平滑 */
/* 关闭抗锯齿,字体边缘犀利。 */
-webkit-font-smoothing:none
/* 字体像素级平滑,在深色背景上会让文字看起来更细了 */
-webkit-font-smoothing:antialiased
/* 字体亚像素级平滑,主要为了在非视网膜设备下更好的显示 */
-webkit-font-smoothing:subpixel-antialiased;
  • IOS 输入英文首字母默认大写,并且默认自动保存:
html
<!-- 通过设置autocapitalize="off"关闭首字母大写 -->
<!-- 使用autocomplete="off"属性关闭自动保存 -->
<input autocapitalize="off" autocomplete="off" autocorrect="off" />
  • IOS 日期格式不支持-分割:统一用/分割 1202/12/12
  • overflow: scroll 或 auto;在 iOS 上滑动卡顿的问题
css
/* 使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会创建一个新的堆栈上下文 */
-webkit-overflow-scrolling: touch;
/* auto 使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止 */
  • 上拉边界下拉出现空白
js
document.body.addEventListener(
  "touchmove",
  function (e) {
    if (e._isScroller) return;
    // 阻止默认事件
    e.preventDefault();
  },
  {
    passive: false,
  }
);
  • 弹出键盘,弹出层被顶上去,光标还停留在原处
js
$("input.van-field__control, textarea.van-field__control").blur(function () {
  setTimeout(function () {
    var currentPosition =
      document.documentElement.scrollTop || document.body.scrollTop;
    window.scrollTo(0, currentPosition); //页面向上滚动
  }, 200);
});

//多个Input/textarea
$(function () {
  var setTimerTop = 0;
  $(document)
    .on(
      "blur",
      "input.van-field__control, textarea.van-field__control",
      function () {
        event.preventDefault();
        setTimerTop = setTimeout(function () {
          window.scrollBy(0, 5); // 页面向上滚动
          window.scrollBy(0, -5);
        }, 500);
      }
    )
    .on(
      "focus",
      "input.van-field__control, textarea.van-field__control",
      function () {
        clearTimeout(setTimerTop);
      }
    );
});

//iframe情况
$(function () {
  var setTimerTop = 0;
  $(document)
    .on(
      "blur",
      "input.van-field__control, textarea.van-field__control",
      function () {
        event.preventDefault();
        setTimerTop = setTimeout(function () {
          parent.scrollBy(0, 5); // 页面向上滚动
          parent.scrollBy(0, -5);
          $("#hide-area-cb").focus();
        }, 500);
      }
    )
    .on(
      "focus",
      "input.van-field__control, textarea.van-field__control",
      function () {
        clearTimeout(setTimerTop);
      }
    )
    .on("focus", "input.van-field__control[disabled]", function () {
      setTimerTop = setTimeout(function () {
        parent.scrollBy(0, 5); // 页面向上滚动
        parent.scrollBy(0, -5);
      }, 500);
    });
});
  • 软键盘唤起,页面 fixed 元素失效
html
<style>
  .warper {
    position: absolute;
    width: 100%;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    /* <!-- 不让页面滚动,而是让主体部分自己滚动 --> */
    overflow-y: scroll;
    -webkit-overflow-scrolling: touch;
  }
  .bottom {
    position: fixed;
    bottom: 0;
    width: 100%;
  }
</style>
<div class="warper">
  <div class="main"></div>
  <div class="bottom"></div>
</div>
  • iOS 闪屏问题
css
* {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
  • input 获取焦点时会放大
  • IOS9+系统下,使用 Viewport 元标记"width=device-width"会导致页面缩小以适应溢出视口边界的内容。可以通过添加"shrink-to-fit=no"到 meta 标签来覆盖此行为,增加的值将阻止页面缩放以适应视口
html
<!-- meta设置user-scalable=no,可取消放大效果.仍然无法阻止页面整体的缩放 -->
<meta
  name="viewport"
  content="width=device-width, initial-scale=1, user-scalable=no, shrink-to-fit=no" />
  • input 域只显示底边框时,会出现两个底部底边圆角效果。设置 border-radius:0 即可
  • 设置 height:100%,如果父级的 flex 值为 1,而没有设置具体高度,则 100%高度设置无效:在父级通过计算来设置具体高度 height,如 height: calc(100% - 100px)

Android

  • 视频没有播放的情况下,双击进度条快进导致 video 无法播放:添加蒙层
  • chrome video 属性返回 Infinity:设置 currentTime 大于实际的 duration
vue
<template>
  <div class="video_box">
    <!-- 解决方法:添加一个蒙层,点击蒙层就播放视频 -->
    <div class="cover" @click.stop.prevent="play" v-if="!played"></div>

    <!-- 
      preload="auto" /* 这个属性规定页面加载完成后载入视频*/
      controls /* 设置是否显示播放器控件(如播放/暂停等) */
      loop="loop" /* 设置或返回视频是否应在结束时再次播放 */
      webkit-playsinline="true"  /* 针对ios9不全屏播放 */
      playsinline="true"  /* 针对ios10、11不全屏播放 */
      /* 启用X5内核同层渲染:视频全屏的时候,div可以呈现在视频层上 */
      x5-video-player-type="h5-page"
      /* 播放器方向,landscape横屏,portraint竖屏,默认值为竖屏 */
      x5-video-orientation="portraint" 
      /* 全屏设置,设置为 true 是防止横屏 */
      x5-video-player-fullscreen="true" 
       /* 设置X5内核为行内播放模式,不能和`x5-video-player-type同时设置会覆盖 */
      x5-playsinline="true" 
      x-webkit-airplay="true" /* 默认不全屏播放 */
     -->

    <video
      :src="src"
      :poster="poster"
      :id="`videoElement`"
      controls
      playsinline
      webkit-playsinline
      webkit-inline
      x5-video-ignore-metadata="true" />
  </div>
</template>

<script>
export default {
  props: {
    src: {
      type: String,
      default: "",
    },
    poster: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      played: false,
      video: null,
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.video = document.getElementById(`videoElement`);
      this.video.addEventListener("loadedmetadata", () => {
        if (this.video.duration === Infinity) {
          // 设置currentTime大于实际的duration,就可以获取video属性了
          this.video.currentTime = 1e101;
          this.video.addEventListener(
            "timeupdate",
            () => {
              console.log("after workaround:", this.video.duration);
              this.video.currentTime = 0;
            },
            { once: true }
          );
        }
      });

      // 获取总时长
      this.video.addEventListener("durationchange", event => {
        console.log(this.video.duration);
        // Infinity
      });

      // 监听缓存进度
      var videoCanskip = 0;
      // 对延迟比较高要求的,可以通过监控 progress 来达到效果
      this.video.addEventListener("progress", function (event) {
        // 返回表示音频/视频已缓冲部分的 TimeRanges 对象。
        var startBuffered = this.video.buffered.start(0);
        var endBuffered = this.video.buffered.end(0);
        videoCanskip = parseInt(endBuffered - startBuffered);
        console.log("video_可快进时长", videoCanskip);
        // 设置currentTime 值 来播放最新内容。注意要控制好频率与快进时长,要不然会导致一直loading加载新的TS片段
        player.currentTime = videoCanskip;
      });
    });
  },
  methods: {
    play() {
      this.video.play();
      this.played = true;
    },
  },
};
</script>

<style lang="scss" scoped>
.video_box {
  position: relative;
  width: 100%;
  height: 194px;
  .cover {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 1000;
  }
  video {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }
}
</style>
  • Android 下取消输入语音按钮
css
input::-webkit-input-speech-button {
  display: none;
}

/* 设置placeholder的颜色 */
::placeholder {
  color: #fff;
}

/* 清除按钮圆角 */
input,
button {
  -webkit-appearance: none;
  border-radius: 0;
}
  • 在 Android 上 placeholder 文字设置行高会偏上:input 有 placeholder 情况下不要设置行高
  • input 域处于焦点状态时,默认会有一圈淡黄色的轮廓 outline 效果:通过设置 outline:none 可将其去除
  • 1 像素边框问题:由于 retina 屏的原因,1px 的 border 会显示成两个物理像素
css
/* CSS3 渐变背景:可以通过渐变背景实现 1px 的 border,实现原理是设置 1px 的渐变背景,50% 有颜色,50% 是透明的 */
@mixin commonStyle() {
  background-size: 100% 1px, 1px 100%, 100% 1px, 1px 100%;
  background-repeat: no-repeat;
  background-position: top, right top, bottom, left top;
}
@mixin border($border-color) {
  @include commonStyle();
  background-image: linear-gradient(
      180deg,
      $border-color,
      $border-color 50%,
      transparent 50%
    ), linear-gradient(
      270deg,
      $border-color,
      $border-color 50%,
      transparent 50%
    ), linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%),
    linear-gradient(90deg, $border-color, $border-color 50%, transparent 50%);
}

/* 伪类 + transform:实现原理是用伪元素高度设置为1px,然后用 transform缩小到原来的一半 */
div {
  position: relative;
  &::after {
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    transform: scaleY(0.5);
    content: "";
  }
}

HTML 识别规则

html
<a href="tel:0755-10086">打电话给:0755-10086</a>
<a href="sms:10086">发短信给: 10086</a>
<a href="mailto:peun@foxmail.com">peun@foxmail.com</a>

<a
  href="iosamap://viewMap?sourceApplication=yukapril&poiname=国宏宾馆&lat=39.905592&lon=116.33604&dev=0"
  >跳转高德地图</a
>
<a
  href="androidamap://viewMap?sourceApplication=yukapril&poiname=国宏宾馆&lat=39.905592&lon=116.33604&dev=0"
  >跳转高德地图</a
>

<!-- 禁止识别电话号码、邮箱、地址 -->
<meta name="format-detection" content="telephone=no,email=no,address=no" />

<!-- 网页会被搜索引擎忽略 -->
<meta name="robots" content="none" />
robots(网页搜索引擎索引方式):对应一组使用逗号(,)分割的值,通常取值:
none:搜索引擎将忽略此网页,等同于noindex,nofollow;
noindex:搜索引擎不索引此网页;nofollow:搜索引擎不继续通过此网页的链接索引搜索其它的网页;
all:搜索引擎将索引此网页与继续通过此网页的链接索引,等同于index,follow;
index:搜索引擎索引此网页;follow:搜索引擎继续通过此网页的链接索引搜索其它的网页;

<!-- 在IOS下,在head元素底部,使用下列代码可以实现添加到主屏幕的功能 -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="Weather PWA" />
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />

<!-- 【QQ浏览器】 -->
// 全屏模式
<meta name="x5-fullscreen" content="true">
// 强制竖屏
<meta name="x5-orientation" content="portrait">
// 强制横屏
<meta name="x5-orientation" content="landscape">
// 应用模式
<meta name="x5-page-mode" content="app">

<!-- 【UC浏览器】 -->
// 全屏模式
<meta name="full-screen" content="yes">
// 强制竖屏
<meta name="screen-orientation" content="portrait">
// 强制横屏
<meta name="screen-orientation" content="landscape">
// 应用模式
<meta name="browsermode" content="application">

根据 MIT 许可证发布