Web开发疑难问题解决方案-(最近更新:2018-11-29)
这篇文章用来记录Web(包括PC和移动端)开发过程中遇到的一些疑难问题的解决方案。
- P1、 '1像素边框'问题
- P2、只读输入框在ios上的莫名表现
- P3、自适应高度的textarea
- P4、 CSS3实现翻牌特效
- P5.IOS 10 以上Safari手机浏览器禁止缩放
- P6 检测浏览器是否为IE
- P7 去掉input[type='number']后面浏览器默认的角标
- P8 将
window.location.relaod
作为setTimeout
的回调函数 - P9 浏览器滚动元素确定
- P10 什么是Plain Object?如何判断对象是Plain Object
- P11 flex最后一行左对齐
- P12 请求按序展示(多个请求,如果一个前面的请求返回的话,不要覆盖后面的请求)
- P13 纯前端实现base64图片下载,兼容IE10+
- P14 body元素上background-gradient不生效
- P15 html5 drop事件触发需要在dragover事件中阻止浏览器默认行为
- P16 点击H5页面元素的时候出现阴影
- P17 img元素作为flex子项高度自适应问题
- P18 webpack4 dynamic import() code split webpackChunkName不生效的问题
- P19 使用
html-webpack-plugin
生成页面js排序问题
P1、 '1像素边框'问题
在平时的开发过程中,经常会遇到UI给的设计稿中元素的边框为0.5px
,这里的0.5px
指的是CSS像素,对于设备像素比为2的屏幕来说,对应的物理像素就是1个像素,也就是说设计稿的本意是让我们实现真实的占据1个物理像素点的边框。由于直接设置对应CSS属性为0.5px
在各平台和浏览器中兼容性很不好,网上给出了很多解决方案,这里仅列出最常用的被论证有效的解决方案。
1.根据设备像素比transform
缩放,常借助伪元素
div {
position: relative;
}
div::after {
content: '';
position: absolute;
z-index: 999;
top: 0;
left: 0;
border: 1px solid red;
transform-origin: 0 0;
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
div::after{
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
/*border-radius: $radius * 2;有圆角*/
}
}
@media (-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3){
div::after{
width: 300%;
height: 300%;
-webkit-transform: scale(.33333);
transform: scale(.33333);
/*border-radius: $radius * 3;有圆角*/
}
}
/**
* @module 背景与边框
* @description 为元素添加边框(包括1px边框)
* @method border
* @version 2.0.0
* @param {String} $border-width 指定边框厚度(单位为px),默认值:1px,取值与`border-width`属性一致,不同方向代表边框位置 <2.0.0>
* @param {String} $border-color 指定边框颜色 <2.0.0>
* @param {String} $border-style 指定边框样式 <2.0.0>
* @param {String} $radius 指定边框圆角半径,默认值:null <2.0.0>
*/
@mixin border($border-width: 1px, $border-color: map-get($base, border-color), $border-style: solid, $radius: null,$opacity: null) {
// 为边框位置提供定位参考
position: relative;
@if $border-width == null {
$border-width: 0;
}
border-radius: $radius;
&::after {
// 用以解决边框layer遮盖内容
pointer-events: none;
position: absolute;
z-index: 999;
top: 0;
left: 0;
// fix当元素宽度出现小数时,边框可能显示不全的问题
// overflow: hidden;
content: "\0020";
border-color: $border-color;
border-style: $border-style;
border-width: $border-width;
@if $opacity != null {
opacity: $opacity;
}
// 适配dpr进行缩放
@media (-webkit-min-device-pixel-ratio:1),(min-device-pixel-ratio:1){
width: 100%;
height: 100%;
@if $radius != null {
border-radius: $radius;
}
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
@if $radius != null {
border-radius: $radius * 2;
}
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
@if $radius != null {
border-radius: $radius * 2;
}
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:3){
width: 300%;
height: 300%;
-webkit-transform: scale(0.33333);
transform: scale(0.33333);
@if $radius != null {
border-radius: $radius * 3;
}
}
-webkit-transform-origin: (0,0);
transform-origin:(0 0);
}
}
div {
position: relative;
border: none;
}
div:after {
content: '';
position: absolute;
left: 0;
background: #000;
widt
h: 100%;
height: 1px;
transform: scaleY(0.5);/*以dpr = 2为例 */
transform-origin: 0 0;
}
// 给styled-compoent使用
/**
* 支持1像素border
*/
export function border(
borderWidth = '1px',
borderColor = '#ccc',
borderStyle = 'solid',
radius,
opacity
) {
// 为边框位置提供定位参考
return `position: relative;
${radius ? 'border-radius: ' + radius + ';' : ''}
&::after {
pointer-events: none;
position: absolute;
z-index: 999;
top: 0;
left: 0;
content: "\\0020";
border-color: ${borderColor};
border-style: ${borderStyle};
border-width: ${borderWidth};
${opacity ? 'opacity: ' + opacity + ';' : ''}
@media (-webkit-min-device-pixel-ratio:1),(min-device-pixel-ratio:1){
width: 100%;
height: 100%;
${radius ? 'border-radius: ' + radius + ';' : ''}
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
width: 200%;
height: 200%;
transform: scale(0.5);
${radius ? 'border-radius: ' + radius * 2 + ';' : ''}
}
@media (-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3){
width: 300%;
height: 300%;
transform: scale(0.33333);
${radius ? 'border-radius: ' + radius * 3 + ';' : ''}
}
transform-origin:0 0;}`
}
参考:
怎么画一条0.5px的边(更新)
再谈Retina下1px的解决方案
P2、只读输入框在ios上的莫名表现
如上左图所示,学校选择输入框是一个只读输入框,整个输入框可点击,包括前面的标签和后面的箭头,点击输入框会隐藏当前页面结构,显示学校选择页面,使用iphone,当点击的区域是input标签区域时,下方会出现输入法的工具条,点击工具条左侧的箭头会弹出键盘,点击完成工具条会隐藏。
<input id="school" readOnly = "readOnly" type="text" placeholder="请选择学校(专科及以上)" />
解决方案
1.使用其他标签模拟一个readOnly
输入框;
2.禁止该输入框touchstart
或者touchend
的默认事件,实践表明禁止click
的默认事件不能解决上述问题
$('#school').on('touchend',(e) => {
e.preventDefault();
...
});
P3、自适应高度的textarea
1.设置div
标签的contenteditable
属性为true
,模拟一个输入框,这么做的缺点是不能完全模拟输入框的表现,如focus
事件自动弹出键盘
2.使用"同步镜像"
html
<div class="reply-input js-reply-input">
<pre class="reply-text-mirror js-reply-text-mirror"></pre>
<textarea class="js-reply-text" placeholder="回复"></textarea>
</div>
css
.reply-input{
position: relative;
width: 82%;
min-height: 92px;
>textarea{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
resize: none;
outline: none;
border: 0;
}
>.reply-text-mirror{
display: block;
white-space: pre-wrap;
word-wrap: break-word;
visibility: hidden;
}
textarea,.reply-text-mirror{
line-height: 20px;
font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
font-size: 14px;
padding: 10px 6px 20px 9px;
min-height: 92px;
width: 100%;
border-radius: 4px;
}
}
javascript
const textarea = $('textarea');
textarea[0].oninput =function() {
var text = textarea.val();
mirror.text(text);
}
textarea
绝对定位,高度与父元素高度相同100%
,通过js操作把输入的字符实时同步到背后的镜像元素来撑高父元素。
P4、 CSS3实现翻牌特效
html
<div id="js-card-Pop" class="m-card-Pop">
<div class="front"></div>
<div class="back"></div>
</div>
css
.m-card-Pop{
position: relative;
.front{
position:absolute;
width: 100%;
height: 100%;
top:0;
left:0;
z-index: 2;
backface-visibility: hidden;
}
.back{ //翻过来后这一边会朝上
transform:rotateY(180deg) translateZ(1px) ;
position:absolute;
width:100%;
height:100%;
top:0;
left:0;
backface-visibility: hidden;
color:#fff;
}
}
.m-card-Pop-R{
transition: 0.5s ease-in-out;
transform-style: preserve-3d;
transform: rotateY(180deg);
}
javascript
$('#js-card-Pop').addClass('m-card-Pop-R');
参考
https://zhuanlan.zhihu.com/p/27089238
P5.IOS 10 以上Safari手机浏览器禁止缩放
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
ios 10以上的Safari浏览器即使加上上述声明页面还是可以双指缩放或者双击缩放,https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html,在微信和Chrome浏览器中表现正常。
网友给出的方法
禁止双指缩放
document.addEventListener('touchmove', function (event) {
if (event.scale !== 1) { event.preventDefault(); }
}, false);
禁止双击缩放
var lastTouchEnd = 0;
document.addEventListener('touchend', function (event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
这两种方法仅供参考,实践过程中可能与页面本身的事件冲突。
如果仅仅要求禁止双击缩放使用css属性touch-action: manipulation;
也是可以的,同时会屏蔽点击事件的300ms延时.
参考
disable viewport zooming iOS 10+ safari?
P6 检测浏览器是否为IE
function isIE() { //ie?
if (!!window.ActiveXObject || "ActiveXObject" in window)
return true;
else
return false;
}
P7 去掉input[type='number']后面浏览器默认的角标
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type="number"]{
-moz-appearance: textfield;
}
P8 将window.location.relaod
作为setTimeout
的回调函数
setTimeout(window.location.reload, 250);
Chrome reports:
Uncaught TypeError: Illegal invocation
需要绑定正确的上下文
setTimeout(window.location.reload.bind(window.location), 250);
P9 浏览器滚动元素确定
scrollTop
, scrollLeft
, scrollWidth
, scrollHeight
都是跟滚动相关的属性。设置 scrollTop
和 scrollLeft
还可以产生滚动。但是不同的浏览器或者Webkit以及不同版本对于页面滚动元素实现不一样,一些认为滚动元素是body
,一些认为滚动元素是html
,实现标准非常混乱,现在一般认为标准的实现应该是html
元素,可能是浏览器厂商们也觉得现在的页面滚动元素太乱,一会儿body
一会儿html
,于是搞出来document.scrollingElement
这么个东西。根据 MDN 的介绍:document
的 scrollingElement
是一个只读属性,始终指向页面滚动元素,但是只要版本较高的浏览器支持该属性。下面的获取方式能兼容大多数场景。
var rootElement = document.scrollingElement || document.body;
参考
Chrome 中 scrollingElement 的变化
P10 什么是Plain Object?如何判断对象是Plain Object
/**
* Checks if `value` is a plain object, that is, an object created by the
* `Object` constructor or one with a `[[Prototype]]` of `null`.
*/
function isPlainObject(value) {
if (Object.prototype.toString.call(value) != "[object Object]") {
return false
}
if (Object.getPrototypeOf(value) === null) {
return true
}
let proto = value
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(value) === proto
}
const obj1 = {}
const obj2 = new Object()
const obj3 = Object.create(null)
const obj4 = Object.create({})
function Foo() {
this.a = 1
}
const obj5 = new Foo()
console.log(
isPlainObject(obj1), // true
isPlainObject(obj2), // true
isPlainObject(obj3), // true
isPlainObject(obj4), // false
isPlainObject(obj5) // false
)
P11 flex最后一行左对齐
"年少无知"的我曾经以为flex布局能够一统天下,无所不能,直到遇到了flex布局最后一行左对齐.
<div id="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
#wrap {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
}
.item {
width: 200px;
height: 100px;
margin-bottom: 40px;
background: blanchedalmond;
}
希望实现多列均匀分布的自适应布局,但是最后一行子项不够,导致不能对齐.
目前最好的解决方案都是多添加几个子项,但是由于是自适应的,我们并不知道需要添加多少个子项,通用的做法是添加"足够多"的子项,并且其对后面的布局影响越小越好,
<div id="wrap">
<div class="item"></div>
<div class="item"></div>
....
....
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
<div class="item-shim"></div>
</div>
....
....
.item-shim {
width: 200px; /*与item尺寸相同*/
height: 0;
/* visibility: hidden; */
}
参考
Flex-box: Align last row to grid
P12 请求按序展示(多个请求,如果一个前面的请求返回的话,不要覆盖后面的请求)
记录每一个请求的发起时间,然后每个请求回来后先判断当前展示在页面上的数据的发起时间,如果本次返回的结果的发起时间晚于当前显示的数据的发起时间,就替换,否则不替换。
(function(){
var lastShowedResult = 0//当前显示的结果的请求时间,初始为0
$('input').keyup(function(){
var requestTime = +Date.now()//每次发请求时记录一下时间,转换成数字,注意在闭包里面
$.get(url, function(data){
if (requestTime > lastShowedResult) {//请求拿到后判断是否晚于当前正显示的内容的发起时间,如果是,则
lastShowedResult = requestTime//替换当前显示结果的发起时间
showResult(data)//替换页面上的内容
}
})
})
})()
作者:谢然
链接:https://www.zhihu.com/question/49470022/answer/118129515
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
P13 纯前端实现base64图片下载,兼容IE10+
// 这里是获取到的图片base64编码,这里只是个例子哈,要自行编码图片替换这里才能测试看到效果
const imgUrl = 'data:image/png;base64,...'
// 如果浏览器支持msSaveOrOpenBlob方法(也就是使用IE浏览器的时候),那么调用该方法去下载图片
// IE浏览器
if (window.navigator.msSaveOrOpenBlob) {
var bstr = atob(imgUrl.split(',')[1])
var n = bstr.length
var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
var blob = new Blob([u8arr])
window.navigator.msSaveOrOpenBlob(blob, 'chart-download' + '.' + 'png')
} else {
// 现代浏览器
const a = document.createElement('a')
a.href = imgUrl
a.setAttribute('download', 'chart-download')
a.click()
}
P14 body元素上background-gradient不生效
body:before {
content:"";
position:fixed;
left:0;
top:0;
right:0;
bottom:0;
z-index:-1;
background:#D8D8D8;
background: linear-gradient(to bottom, #D8D8D8, #585858) ;
}
P15 html5 drop事件触发需要在dragover事件中阻止浏览器默认行为
document.addEventListener("dragover", function(event) {
// prevent default to allow drop
event.preventDefault();
}, false);
document.addEventListener("drop", function(event) {
// prevent default action (open as link for some elements)
event.preventDefault();
}, false);
P16 点击H5页面元素的时候出现阴影
.class{
-webkit-tap-highlight-color:transparent;
}
P17 img元素作为flex子项高度自适应问题
<div style="display:flex;">
<img src="https://i.imgur.com/hHzrRsf.jpg" style="width:160px;">
</div>
<div style="display:flex;">
<img src="https://i.imgur.com/hHzrRsf.jpg" style="width:100%;">
</div>
上述代码只设置了图片的宽度定宽或者为父元素的宽度,本意是高度自适应,保持宽高比,从而避免图片扭曲,但是在低版本的chrome浏览器包括Android浏览器中(chrome 78已修复),高度会是图片的原始高度
解决方案
<div style="display:flex;">
<img src="https://i.imgur.com/hHzrRsf.jpg" style="width:160px;align-self: center;">
</div>
低版本的chrome浏览器错误的将img
的align-self
解析成stretch
参考
Image height: auto doesn't work on chrome
P18 webpack4 dynamic import() code split webpackChunkName不生效的问题
首先必须配置webpack.config.js
{
output: {
chunkFilename: "[name].bundle.js"
},
}
import(/* webpackChunkName: "byted-bridge-app-jssdk" */ "@bridge/byted-bridge-app-jssdk"),
如果使用babel保证.babalrc
不能移除注释
{
"comments": true
}
如果使用typescript,保证tsconfig
{
module: "esnext",
removeComments: false
}
参考
webpack dynamic import naming doesn't work
how can i use ts-loader and webpack3 dynamic imports to implement code-splitting
P19 使用html-webpack-plugin
生成页面js排序问题
假设我们有如下的webpack配置
entry: {
'demo': './src/demo.ts',
'hysdk': './src/hysdk.ts'
},
plugins: [
new HtmlWebpackPlugin()
]
默认情况下生成的js插入html文件的顺序如下
<script type="text/javascript" src="demo.bundle.js"></script>
<script type="text/javascript" src="hysdk.bundle.js"></script>
假设demo.bundle.js
必须依赖hysdk.bundle.js
才能执行,显然需要调整脚本顺序。有三种方法
1.修改配置
手动排
new HtmlWebpackPlugin({
...
chunks: ['hysdk', 'demo'],
chunksSortMode: 'manual'
})
自动排
new HtmlWebpackPlugin({
...
chunksSortMode: function (chunk1, chunk2) {
var order = ['hysdk', 'demo'];
var order1 = order.indexOf(chunk1.names[0]);
var order2 = order.indexOf(chunk2.names[0]);
return order1 - order2;
}
})
2.修改模板和配置
<!-- ./src/index.template.html -->
<html>
<body>
<script src="<%= htmlWebpackPlugin.files.chunks.hysdk.entry %>"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.demo.entry %>"></script>
</body>
</html>
new HTMLWebpackPlugin({
template: './src/index.template.html',
inject: false,
}),
Web开发疑难问题解决方案-(最近更新:2018-11-29)的更多相关文章
- Web开发入门疑问收集(不定期更新)
bootstrap container和container-fluid的区别 原始链接 container 根据显示设备满足的最小宽度,来决定实际内容宽度,是一个根据设置内容阶梯式响应的布局. 例子: ...
- 2018.11.29 Wireless technology roadmap(1)
1物联网还是卖开发板阶段. BT NBIoT 窄带物联网(Narrow Band Internet of Things) Sigfox Zigbee Wifi:大数据量传输 LoRA :远距离无线电( ...
- 2018.11.29 Struts2中拦截器的学习&项目的实际运用
struts2官方架构 拦截器的创建 第一种方式 第二种方式 看源码AbstractInterceptor 底层已经帮我们写过这些方法了 第三种方式(推荐) 拦截器API学习 放行 前后处理 不放行, ...
- 移动端 Web 开发前端知识整理
文章来源: http://www.restran.net/2015/05/14/mobile-web-front-end-collections/ 最近整理的移动端 Web 开发前端知识,不定期更新. ...
- 移动web开发研究
1.jQuery Mobile jQuery Mobile框架能够帮助你快速开发出支持多种移动设备的Mobile应用用户界面.jQuery Mobile最新版本是1.4.0,默认主题采用扁平化设计风格 ...
- web开发工具flask中文英文书籍-持续更新
web开发工具flask中文英文书籍-持续更新 python测试开发_AI命理关注 0.9222018.11.10 07:48:43字数 625阅读 885 python测试开发项目实战-目录 pyt ...
- 移动Web开发的bug及解决方案
我目前移动Web开发遇到的bug以及解决方案(慢慢补充当中). 1.android4.0以上一部分手机的webview中,当canvas小于屏幕大小时,绘图时会出现重影,就是说一个图只绘制了一遍,却出 ...
- Web项目开发性能优化解决方案
web开发性能优化---安全篇 1.ip验证 2.操作日志.安全日志.登录日志 3.SQL注入校验 4.权限管理 5.验证规范(前端.后端.数据库约束) 2014-10-29 08:04 2773 ...
- Web开发之分页算法,N(N>=3)种解决方案
在Web开发中,分页是最常用的一个功能了.但是,想简单.灵活.方便地解决分页这个问题,还是需要动点脑筋的. 今天,简要梳理下,过去6年中,亲自遇到的分页问题和解决办法. 1. 定义问题 原由:在页面中 ...
随机推荐
- 内网MySQL YUM源记录
#mysql yum */180 * * * * rsync -av --delete rsync://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-con ...
- 重新安装了环境报错{"error":"could not find driver"}
前言:最近新工作开发oa系统,没有借助工具安装wamp环境,结果在测试项目时候出现了bug,找了很久,发现方向没有对 报错信息: {"error":"could not ...
- [面试]volatile类型修饰符/内存屏障/处理器缓存
volatile类型修饰符 本篇文章的目的是为了自己梳理面试知识点, 在这里做一下笔记. 绝大部分内容是基于这些文章的内容进行了copy+整理: 1. http://www.infoq.com/cn/ ...
- python 黑魔法收集--已结
awesome python 中文大全 Fabric , pip, virtualenv 内建函数好文 awesome python 奇技淫巧 一句话求阶乘 from functools import ...
- [Android] Android 手机下 仿 今日头条 新闻客户端
利用一个月的时间,自学了 Android 开发 ,为了检验学习成果,特意 开发了这个 仿 今日头条 新闻客户端 AppNews 包括图文新闻+视频新闻+图片新闻 预览演示如下: 功能说明: 1)底部 ...
- 《B2C商城》电商平台搭建流程分析
商城网站建设在当今互联网时代中是非常重要的.商城网站,是企业产品展示.品牌宣传与消费者互动交流的一个平台,利用好这样的一个平台,就能占得先机.那么问题来了,商城网站如何建设呢?这对于企业来说真的是一个 ...
- 【译】使用 Flutter 实现跨平台移动端开发
作者: Mike Bluestein | 原文地址:[https://www.smashingmagazine.com/2018/06/google-flutter-mobile-developm ...
- 推送提交(git push)
当需要同别人共享某个分支上的工作成果时,就要把它推送到一个具有写权限的远程仓库.你的本地分支并不会自动同步到远程仓库,必须要显式地推送那些你想要与别人共享的分支.这样一来,你可以使用私有分支做一些不想 ...
- javascript浏览器事件
1. Js浏览器对象层级结构:Window 窗口对象(location地址对象.document文档对象(Form表单对象(表单控件))) 2. Window对象属性: 名称 描述 documen ...
- http 状态码大全
状态码大全 1**(信息类):表示接收到请求并且继续处理 100——客户必须继续发出请求 101——客户要求服务器根据请求转换HTTP协议版本 2**(响应成功):表示动作被成功接收.理 ...