总结前端工作当中遇到的那些坑

总结前端工作当中遇到的那些坑

微信相关类

微信公众号使用js-sdk开发常见问题及解决方案

微信公众号使用js-sdk接口调用频次限制说明

获取openid

微信内H5调起支付

事件相关类

移动端点击穿透问题

图片加载出现403错误

原因:在http中请求https图片可能会出现403错误

解决方案:在htmlhead标签中加入<meta name="referrer" content="no-referrer" />

阻止浏览器当前页面回退操作

window.history.pushState('forward', null, window.location.href); // 首先在当前页面创建一个新的history实体
window.addEventListener('popstate', () => { // 监听状态变化
    window.history.forward(1); // 跳转到下一个history
});

onload事件只能绑定在window上,绑定到document上不生效

在开发时遇到一个问题:使用document绑定onload事件没有生效,具体代码如下:

// onload事件绑定在document上没有生效
document.addEventListener('load', function () {
    console.log(111);
});

// onload事件绑定在window上才生效
window.addEventListener('load', function () {
    console.log(222);
});

React中使用defaultValue、defaultChecked遇到的问题

The defaultValue and defaultChecked props are only used during initial render. If you need to update the value in a subsequent render, you will need to use a controlled component. 所以如果有值是会变的 只能用value/checked。 不能用defaultValue/defaultChecked

前端页面弹窗禁止遮罩底部页面滚动(移动端兼容ios问题)

// 判断设备类型
function whatDevice () {
    let device = '';
    let ua = window.navigator.userAgent.toLowerCase();
    if (/MicroMessenger/i.test(ua)) {
        device = 'wx';
    } else if (/(iPhone|iPad|iPod|iOS)/i.test(ua)) {
        device = 'ios';
    } else if (/(Android)/i.test(ua)) {
        device = 'android';
    }
    return device;
}

// 禁止遮罩底部页面滚动, fixed 固定, scroll 滚动
function fixScroll (type) {
    var device = whatDevice();
    if (device === 'android') {
        if (type === 'fixed') {
            // 会引起页面重绘
            var scrollTop = document.documentElement.scrollTop + document.body.scrollTop;
            window.localStorage.setItem('scroll', JSON.stringify({
                originHtmlScrollY: document.getElementsByTagName('html')[0].style.overflowY,
                originHtmlPosition: document.getElementsByTagName('html')[0].style.position,
                scrollTop: document.documentElement.scrollTop + document.body.scrollTop
            }));
            document.getElementsByTagName('html')[0].style.overflowY = 'hidden';
            document.getElementsByTagName('html')[0].style.position = 'fixed';
            document.getElementsByTagName('html')[0].style.top = '-' + scrollTop + 'px';
        } else if (type === 'scroll') {
            var originHtmlScroll = JSON.parse(window.localStorage.getItem('scroll')) || {
                originHtmlScrollY: '',
                originHtmlPosition: '',
                scrollTop: 0
            };
            window.localStorage.removeItem('scroll');
            document.getElementsByTagName('html')[0].style.overflowY = originHtmlScroll.originHtmlScrollY;
            document.getElementsByTagName('html')[0].style.position = originHtmlScroll.originHtmlPosition;
            window.scrollTo(0, originHtmlScroll.scrollTop);
        }
    } else {
        // 兼容ios弹窗遮罩层底部页面禁止滑动处理(还是有问题啊,滑动弹窗遮罩层底部页面还是会滚动)
        if (type === 'fixed') {
            // 获取遮罩层页面node节点
            var maskNode = document.getElementsByClassName('mask');
            maskNode && maskNode[0] && maskNode[0].addEventListener('touchstart', function (e) {
                e.stopPropagation();
                e.preventDefault();
            });
        }
    }
}

上传图片格式转换(react)

<input className='photo-upload-tip'
        type='file'
        ref={(input) => { this.fileInput = input; }}
        onChange={this.handleUploaderImgChangeTest} />
// base64格式转成blob格式
function dataURItoBlob (dataURI) {
    let byteString = window.atob(dataURI.split(',')[1]);
    let mimeString = dataURI.split(',')[0].split(':')[1].split('')[0];
    let ab = new ArrayBuffer(byteString.length);
    let ia = new Uint8Array(ab);
    for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    console.log('blob', new Blob([ab], {type: mimeString}));
    return new Blob([ab], {type: mimeString});
}

// 把base64格式转成formData格式
function base64toFormData (imageBase64) {
    let blob = dataURItoBlob(imageBase64);
    // let canvas = document.createElement('canvas');
    // let dataURL = canvas.toDataURL('image/png', 0.5);
    let fd = new FormData();
    fd.append('file', blob);
    return fd;
}

handleUploaderImgChangeTest = () => {
    let formData = new FormData();
    formData.append('file', this.fileInput.files[0]);
    fetch('http://dev-wxuser-api.wanshifu.com/common/upload/uploadimage', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
        },
        credentials: 'include',
        body: formData,
    }).then((res) => {
        console.log('success', res.status);
    }).catch((e) => {
        console.log('error', e);
    });
 }

兼容相关类

移动端动态节点绑定事件ios点击失效

原因是:

参考博客:

在移动端中使用css3部分动画时可能会出现加载页面宽度抖动问题以及ios9滚动条区域空白处理

解决方案:在html顶级div中加入样式overflow-x: hidden;

移动端手机浏览器滑动页面不流畅

-webkit-overflow-scrolling属性控制元素在移动设备上是否使用滚动回弹效果.

解决方案:在页面body标签,增加样式-webkit-overflow-scrolling: touch;

body {
    -webkit-overflow-scrolling: touch;
    overflow-scrolling: touch;
}

ios点击事件出现灰色背景闪烁解决方案

-webkit-tap-highlight-color是一个没有标准化的属性,能够设置点击链接的时候出现的高亮颜色。显示给用户的高光是他们成功点击的标识,以及暗示了他们点击的元素。

解决方案:在页面body标签,增加样式-webkit-tap-highlight-color: transparent;

body {
    /* 防止iOS的Safari浏览器点击出现灰色背景闪烁 */
    -webkit-tap-highlight-color: transparent;
}

优化移动端点击300ms延时

touch-action也经常用于完全解决由支持双击缩放手势引起的点击事件的延迟。

html {
  touch-action: manipulation;
}

部分安卓手机点击img图片会全屏放大显示问题(变成全屏查看图片模式了)

pointer-events值none表示鼠标事件“穿透”该元素并且指定该元素“下面”的任何东西。

解决方案:在img标签上设置pointer-events: none;样式来屏蔽图片上所有的事件,如果在图片上有绑定事件,则在图片上增加一层div,把事件绑定到该div上

ios内嵌h5页面点击form表单input获取焦点以后页面会放大问题

解决方案:

1、在html页面head标签加入

<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0">

2、把input字体设置16px以上

微信H5页面ios遮罩弹出框input调用键盘收起键盘后底部出现空白以及样式错位问题

如图:

ios遮罩弹出框input调用键盘样式问题

解决方案:在input输入框失去焦点的时候触发一下底部页面的滚动事件

inputBlur() {
    let ua = window.navigator.userAgent.toLowerCase();
    if (/(iPhone|iPad|iPod|iOS)/i.test(ua)) {
        window.scrollTo(0, 0);
    }
}

H5页面input占位符placeholder在ios手机上没有垂直居中问题

如图:

ios遮罩弹出框input调用键盘样式问题

解决方案:设置line-height: normal, 如果还是不行,设置line-height: 1px

禁止ios10及以上h5页面缩放

移动端禁止页面缩放我们通常使用<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0">就可以解决,但是在ios10以上有兼容问题,具体解决方案如下:

// 禁止ios10及以上页面缩放
window.onload = function() {
    // 阻止双击放大
    var lastTouchEnd = 0;
    document.addEventListener('touchstart', function(event) {
        if (event.touches.length > 1) {
            event.preventDefault();
        }
    });
    document.addEventListener('touchend', function(event) {
        var now = (new Date()).getTime();
        if (now - lastTouchEnd <= 300) {
            event.preventDefault();
        }
        lastTouchEnd = now;
    }, false);

    // 阻止双指放大
    document.addEventListener('gesturestart', function(event) {
        event.preventDefault();
    });
}

H5页面微信浏览器ios手机实现音频自动播放功能

问题:H5页面ios系统音频不能自动播放,但是安卓手机可以

解决方案:在微信浏览器中使用微信环境相关api实现ios手机自动播放音频功能

注意:因为是通过调用微信浏览器相关api,所以只能在微信中有效

<!-- 如果是使用js-sdk中的api,则需要引入改JS -->
<script src='https://res.wx.qq.com/open/js/jweixin-1.4.0.js'></script>

<!-- audio音频 -->
<audio id="shakemusic" src="/static/music/red1.mp3" style="display: none;"></audio>
// 不需要引入js-sdk
/** 
 * @desc 微信ios实现音频自动播放
 * @param audioId audio音频id
 */
wxIosAutoPlay(audioId) {
    let ua = window.navigator.userAgent;
    let isWx = /MicroMessenger/i.test(ua);
    let isIos = /(iPhone|iPad|iPod|iOS)/i.test(ua);
    if (isWx && isIos) {
        document.addEventListener("WeixinJSBridgeReady", () => {
            document.getElementById(audioId).play();
        }, false);
    }
}
wxIosAutoPlay('shakemusic');

// 需要引入js-sdk
wx.config({
    // 配置信息, 即使不正确也能使用 wx.ready
    debug: false,
    appId: '',
    timestamp: 1,
    nonceStr: '',
    signature: '',
    jsApiList: []
});
wx.ready(function() {
    document.getElementById('shakemusic').play();
});

ios9使用flex布局图片设置内边距导致图片变形

如果你不希望某个容器在任何时候都不被压缩,那设置flex-shrink:0;

解决方案:使用flex布局属性flex-shrink属性,把该值设置为0,如图:

ios9使用flex布局图片设置内边距导致图片变形

深入理解css3中的flex-grow、flex-shrink、flex-basis

new Date(‘yyyy-mm-dd hh:mm:ss’).getTime()在苹果手机、Safari浏览器不兼容问题

问题:在安卓手机chrome浏览器可以使用2018-10-10 10:10:10这样的日期格式计算毫秒数,而在苹果手机Safari浏览器中不能这样使用日期字符串格式,只能使用2018/10/10 10:10:10这种格式,但是可以使用2018-10-10这种不带时间格式的字符串

解决方法:new Date(data.replace(/-/g,’/’)).getTime()

// 在chrome浏览器运行

// 这两种格式毫秒数不一样
let pastTime = new Date('2013-12-16').getTime(); // 1387152000000
let pastTime = new Date('2013/12/16').getTime(); // 1387123200000

let pastTime = new Date('2013-12-16 00:00:00').getTime(); // 1387123200000
let pastTime = new Date('2013/12/16 00:00:00').getTime(); // 1387123200000

// 在safari浏览器运行

let pastTime = new Date('2013-12-16').getTime(); // 1387152000000
let pastTime = new Date('2013/12/16').getTime(); // 1387123200000

let pastTime = new Date('2013-12-16 00:00:00').getTime(); // NaN
let pastTime = new Date('2013/12/16 00:00:00').getTime(); //1387123200000

总结:2013/12/16默认是从0点0分0秒开始计算毫秒数

canvas.toBlob()低版本兼容ployfill

// canvas.toBlob ployfill
if (!HTMLCanvasElement.prototype.toBlob) {
    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
        value: function (callback, type, quality) {
            var dataURL = this.toDataURL(type, quality).split(',')[1];
            setTimeout(function () {
                var binStr = atob(dataURL);

                var len = binStr.length;

                var arr = new Uint8Array(len);

                for (var i = 0; i < len; i++) {
                    arr[i] = binStr.charCodeAt(i);
                }

                callback(new Blob([arr], {type: type || 'image/png'}));
            });
        }
    });
}

样式动画相关类

相同动画属性逐帧动画会覆盖transition过渡动画设置的属性

    /* 相同动画属性逐帧动画会覆盖transition过渡动画设置的属性 */

    /* 过度动画transition属性设置transform过渡效果 */
    .avatar {
        transition: all 500ms;
        transform: rotateZ(0) !important;
    }

    .avatar:hover {
        transform: rotateZ(360deg) !important;
    }

    /* 逐帧动画设置transform属性动画效果 */
    @keyframes rightToLeftRotate {
        0% {
            opacity: 0;
            transform: translateX(200%) rotateZ(720deg);
        }

        100% {
            transform: none;
        }
    }

    .rtlr-delay500 {
        animation:  rightToLeftRotate 1000ms ease-in both;
    }

总结:当这两个class作用于同一dom节点时,transition过度动画设置的transform属性不会生效

参考博客

赞 赏