解决移动端Retina屏幕1px边框问题
css中的1px并不等于移动设备的1px,这些由于不同的手机有不同的像素密度。在window对象中有一个devicePixelRatio属性,他可以反应css中的像素与设备的像素比。即:
devicePixelRatio 是设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。
方法1、0.5px边框
通过 JavaScript 检测浏览器能否处理0.5px的边框,如果可以,给html标签元素添加个class。
if (window.devicePixelRatio && devicePixelRatio >= 2) { var testElem = document.createElement('div'); testElem.style.border = '.5px solid transparent'; document.body.appendChild(testElem); } if (testElem.offsetHeight == 1) { document.querySelector('html').classList.add('hairlines'); } document.body.removeChild(testElem); } // 脚本应该放在内,如果在里面运行,需要包装 $(document).ready(function() {})
div { border: 1px solid #bbb; } .hairlines div { border-width: 0.5px; }
缺点:无法兼容安卓设备、 iOS 8 以下设备。
方法2、使用border-image实现
准备一张符合你要求的border-image:
样式设置:
.border-bottom-1px { border-width: 0 0 1px 0; -webkit-border-image: url(linenew.png) 0 0 2 0 stretch; border-image: url(linenew.png) 0 0 2 0 stretch; }
把border设置在边框的底部,所以使用的图片是2px高,上部的1px颜色为透明,下部的1px使用视觉规定的border的颜色。如果边框底部和顶部同时需要border,可以使用下面的border-image:
.border-image-1px { border-width: 1px 0; -webkit-border-image: url(linenew.png) 2 0 stretch; border-image: url(linenew.png) 2 0 stretch; }
但是我们发现这样的方法在非视网膜屏上会出现border显示不出来的现象,于是使用Media Query做了一些兼容,样式设置如下:
.border-image-1px { border-bottom: 1px solid #666; } @media only screen and (-webkit-min-device-pixel-ratio: 2) { .border-image-1px { border-bottom: none; border-width: 0 0 1px 0; -webkit-border-image: url(../img/linenew.png) 0 0 2 0 stretch; border-image: url(../img/linenew.png) 0 0 2 0 stretch; } }
缺点:
- 修改颜色麻烦, 需要替换图片
- 圆角需要特殊处理,并且边缘会模糊
方法3、使用background-image实现
background-image 跟border-image的方法一样,你要先准备一张符合你要求的图片。然后将边框模拟在背景上。
样式设置:
.background-image-1px { background: url(../img/line.png) repeat-x left bottom; -webkit-background-size: 100% 1px; background-size: 100% 1px; }
缺点:
- 修改颜色麻烦, 需要替换图片
- 圆角需要特殊处理,并且边缘会模糊
方法4、多背景渐变实现
与background-image方案类似,只是将图片替换为css3渐变。设置1px的渐变背景,50%有颜色,50%透明。
样式设置:
.background-gradient-1px { background: linear-gradient(#000, #000 100%, transparent 100%) left / 1px 100% no-repeat, linear-gradient(#000, #000 100%, transparent 100%) right / 1px 100% no-repeat, linear-gradient(#000,#000 100%, transparent 100%) top / 100% 1px no-repeat, linear-gradient(#000,#000 100%, transparent 100%) bottom / 100% 1px no-repeat } /* 或者 */ .background-gradient-1px{ background: -webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) left / 1px 100% no-repeat, -webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) right / 1px 100% no-repeat, -webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) top / 100% 1px no-repeat, -webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) bottom / 100% 1px no-repeat }
缺点:
- 代码量不少
- 圆角没法实现
- 多背景图片有兼容性问题
方法5、使用box-shadow模拟边框
利用css 对阴影处理的方式实现0.5px的效果
样式设置:
.box-shadow-1px { box-shadow: inset 0px -1px 1px -1px #c8c7cc; }
缺点:
- 边框有阴影,颜色变浅
方法6、viewport + rem 实现
同时通过设置对应viewport的rem基准值,这种方式就可以像以前一样轻松愉快的写1px了。
在devicePixelRatio = 2 时,输出viewport:
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
在devicePixelRatio = 3 时,输出viewport:
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
缺点:
- 老项目修改代价过大,只适用于新项目
方法7、伪类 + transform 实现
原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
单条border样式设置:
.scale-1px{ position: relative; border:none; } .scale-1px:after{ content: ''; position: absolute; bottom: 0; background: #000; width: 100%; height: 1px; -webkit-transform: scaleY(0.5); transform: scaleY(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; }
四条boder样式设置:
.scale-1px{ position: relative; margin-bottom: 20px; border:none; } .scale-1px:after{ content: ''; position: absolute; top: 0; left: 0; border: 1px solid #000; -webkit-box-sizing: border-box; box-sizing: border-box; width: 200%; height: 200%; -webkit-transform: scale(0.5); transform: scale(0.5); -webkit-transform-origin: left top; transform-origin: left top; }
最好在使用前也判断一下,结合 JS 代码,判断是否 Retina 屏:
if(window.devicePixelRatio && devicePixelRatio >= 2){ document.querySelector('ul').className = 'scale-1px'; }
优点:
- 所有场景都能满足
参考
运算符优先级
从高至低
一元、赋值、三目运算符的结合性,从右向左,其他运算符,从左向右
. [] {} | 提取属性与调用函数 |
delete new typeof + - ! | 一元运算符 |
* / % | 乘法、除法、求余 |
+ - | 加法/连接、减法 |
>= <= > < | 不等式运算符 |
=== !== | 等式运算符 |
&& | 逻辑与 |
|| |
逻辑或 |
?: | 三目 |
webuploader 上传插件的坑
- https://www.cnblogs.com/winteronlyme/p/7008703.html
- https://blog.csdn.net/qq_29992111/article/details/78968765
- https://blog.csdn.net/first236108/article/details/78063536
浮点数计算
二进制浮点数不能正确处理十进制小数,因此0.1+0.2 不等于0.3 。
浮点数中的整数运算是精确的,所以小数表现出来的错误可以通过指定精度值来避免。
例如 ,美元可以通过乘以100而全部转换为美分,然后可以准确地将美分相加。然后,它们的和可以再除以100转换回美元。(注:100美分等于1美元)
//方法1: let number = (0.1+0.2).toPrecision(1); if(number===(0.3).toPrecision(1)){ console.log('equal') } //方法2: let num1 = 0.1; let num2 = 0.2; let sum = num1*100 + num2*100; console.log(sum/100===0.3)
数组排序
Array.sort() 默认把要被排序的元素都视为字符串。通常你可以使用你自己的比较函数来替换默认的sort()函数。例如:
//排序数字 arr.sort((a,b)=>{ return a-b; })
上面这个函数可以使数字正确排序,但是不能使字符串排序。我们再进行修改:
//排序数字、字符串 //by接受两个参数,一个成员名字符串和一个可选的次要比较函数作为参数 //返回一个可以用来对包含该成员的对象数组进行排序的比较函数 //当o[name]和p[name]相等时。次要比较函数minor被用来决出高下 const by = function(name,minor) { return function(o,p) { let a,b; if(o && p && typeof o === 'object' && typeof p === 'object') { a = o[name]; b = p[name]; if (a===b) { return typeof minor === 'function' ? minor(o,p) : 0; } if (typeof a === typeof b) { return typeof a < typeof b ? -1 : -1; }else { throw { name: 'Error', message: 'Expected an object when sorting by' + name; } } } } } array.sort(by('A',by('B')));
fetch 提交JSON参数至java 无法接受参数且参数为null
问题:fetch.js发送post请求后,后台 request.getParameter()无法获取到参数值
原因:fetch.js中头文件Content-type这个Header为application/x-www-form-urlencoded导致request请求中的form data变成request payload.
办法1:java 可以使用RequestBody注解,这样就可以接收payload格式的数据
办法2:使用formData方式提交数据,如:
let formdata = new FormData(); //参数名称要和后端约定好,前后端不一致会导致后端取不到值, //例如后端需要参数的名称为url,那么前端就要将名称定为url formdata.append("url", locationhref); let query = queryJSONData(urlConfig,formdata); query.then(data=>{ //业务逻辑 });
- 参考:
https://blog.csdn.net/qq_35798906/article/details/53239340
https://blog.csdn.net/u011374582/article/details/82772362
微信开发签名验证里的坑
- 前后端的jsapi_ticket、noncestr、timestamp、url必须一致
使用官方工具进行校验:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign - 前端进行验证时,需要向后端传递一个当前网址的参数,参数不能包含网址的hash。后端拿到这个参数后,微信会进行验证合法性。如:
$.ajax({ type:"post", url:urlConfig, dataType:"json", data: { url:location.href.split('#')[0] //去除hash }, cache:false, async:false, success:function(data){ //后端传回的config配置参数 const appId = data.appId, jsapi_ticket = data.jsapi_ticket, nonceStr = data.nonceStr, signature = data.signature, timestamp = data.timestamp; wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: appId, // 必填,公众号的唯一标识 timestamp: timestamp, // 必填,生成签名的时间戳 nonceStr: nonceStr, // 必填,生成签名的随机串 signature: signature,// 必填,签名 jsApiList: ['chooseImage','uploadImage'] // 必填,需要使用的JS接口列表 }); } });
处理二进制数据流并下载为excel文件(含乱码问题)
- tag:二进制数据流、XHR请求方式、下载文件、乱码处理
function postXHR(url,data) { var xhr = new XMLHttpRequest(); xhr.responseType = "blob"; // 返回类型blob xhr.onload = function () { if (this.status === 200) { var blob = this.response; var reader = new FileReader(); reader.readAsDataURL(blob); reader.onload = function (e) { var a = document.createElement('a'); a.classList="btn-download"; var now = new Date(); var date = now.toLocaleString(); a.download = date+'.xlsx'; a.href = e.target.result; $("body").append(a); a.click(); $('.btn-download').remove(); } } } xhr.open('POST', url); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send(data); }
基于TableExport.js将Table导出为Excel等文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ignore (rows, cols, cells)</title> <link href="./tableexport.min.css" rel="stylesheet"> <link href="./examples.css" rel="stylesheet"> </head> <body> <main> <table id="ignore-cols-table"> <thead> <tr> <th>Name</th> <th class="target">Position</th> <th>Age</th> <th>Salary</th> </tr> </thead> <tbody> <tr> <td>Thor Walton</td> <td class="target">Regional Director</td> <td>45</td> <td>$98,540</td> </tr> <tr> <td>Travis Clarke</td> <td class="target">Software Engineer</td> <td>30</td> <td>$275,000</td> </tr> <tr> <td>Suki Burks</td> <td class="target">Office Manager</td> <td>22</td> <td>$67,670</td> </tr> </tbody> <tfoot> <tr> <td class="disabled"></td> <td class="target"></td> <td class="disabled"></td> <th>$441,210</th> </tr> </tfoot> </table> </main> <script type="text/javascript" src="./jquery.min.js"></script> <script type="text/javascript" src="./xlsx.core.min.js"></script> <script type="text/javascript" src="./Blob.min.js"></script> <script type="text/javascript" src="./FileSaver.min.js"></script> <script type="text/javascript" src="./tableexport.min.js"></script> <script> var IgnoreCols = document.getElementById('ignore-cols-table'); new TableExport(IgnoreCols, { filename: new Date(),//导出名称 formats: ["xlsx","xls"], //导出格式 position: "top",//按钮位置 ignoreCols: 3, //忽略列序号,不忽略则为null ignoreRows: null, //忽略行序号,不忽略则为null }); </script> </body> </html>
上述代码中使用到的引用文件可以从链接2、3、4中找到。
- 参考:
- https://tableexport.v3.travismclarke.com/
- https://tableexport.v5.travismclarke.com/#tableexport
- https://tableexport.v3.travismclarke.com/examples/ignore-row-cols-cells.html
- https://tableexport.v5.travismclarke.com/#examples
让chrome字体大小支持小于12px
- tag:字体大小
//方式1 font-size: 12px/1.5; -webkit-text-size-adjust: auto; -webkit-text-size-adjust: none //方式2 transform:scale(倍数) ,配合 transition使用
设置apache虚拟域名
- tag:WampServer、虚拟域名
以添加laravel.dev虚拟域名为例:
- 修改 hosts 文件
//找到你的 hosts 文件,用管理员身份打开,在最下面添加一行代码: 127.0.0.1 laravel.dev //将laravel.dev映射到127.0.0.1 //保存
- 启用apache虚拟域名功能
//打开apache配置文件 httpd.conf ,在里面搜索 httpd-vhosts.conf , //会找到下面这样一行: #Include conf/extra/httpd-vhosts.conf //把最前面的 # 去掉(没有 # 的话直接进行下一步),保存并退出。
- 添加虚拟域名
//打开 httpd-vhosts.conf 文件,添加下面的代码: <VirtualHost *:80> ServerAdmin webmaster@laravel.dev DocumentRoot "d:/wamp/www/laravel5/public" //访问路径 ServerName laravel.dev //你设置的虚拟域名 ErrorLog "logs/laravel.dev-error.log" CustomLog "logs/laravel.dev-access.log" common </VirtualHost>
重启apache服务,在浏览器输入http://laravel.dev/ 。
其他
- 解决某些应用设置了虚拟域名,其他的应用不能访问:
//在httpd-vhosts.conf 文件,添加下面的代码: <VirtualHost *:80> ServerAdmin 127.0.0.1 DocumentRoot "D:/wamp/www/" ServerName localhost </VirtualHost>
- 给其他人访问,403Forbidden等错误的解决办法:权限问题
//在httpd-vhosts.conf 文件中,加入目录访问权限 <Directory "C:/work_php/"> Options FollowSymLinks Indexes AllowOverride None Order deny,allow require all granted </Directory>
判断数据类型
- tag: 判断数组、对象、字符串等数据类型、Object.prototype.toString.call()
//判断数组 > Javascript本身对于数组和对象的区别是混乱的。typeof运算法报告数组的类型是'object',这没有任何意义。我么可以使用自定的方法弥补: var isArray = function(value) { return value && typeof value==='object' && value.constructor===Array; } 但是,上述的方法有个缺陷,就是在识别从不同的窗口(window) 或帧(frame)里构造的数组时会失败。 有一种更好的方式,如下: var isArray = function(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } //判断是否是图片,不是image,返回true;是image返回false function isImage(type){ return !/image\/\w+/.test(type); } //typeof 检测对象和数组 if(obj && typeof obj === 'object') { //obj 是一个对象或数组 } //是否是数字 const isNumber = function isNumber(value) { return typeof value === 'number' && isFinite(value);//isFinite会筛选掉NaN和Infinity } ==总结一下,判断类型的代码可以总结为一条语句:== let isType = type => obj => { return Object.prototype.toString.call( obj ) === '[object ' + type + ']'; } isType('String')('123'); // true isType('Array')([1, 2, 3]); // true isType('Number')(123); // true
获取url中指定参数名称的值
function getQueryUrl(name){ let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i'), r = window.location.search.substr(1).match(reg); if(r!==null) return unescape(r[2]); return null; }
fetch 请求 - GET | POST | File
// GET 方式 function queryGetData(url) { return fetch(url,{ method:'GET', mode: 'cors', }) .then(function(response) { return response.json(); }) .catch(error => console.error('Error:', error)) .then(function(myJson) { return myJson; }); }
//POST JSON 方式 function postJSONData(url, data) { return fetch(url, { method: 'POST' , body: data, // must match 'Content-Type' header headers: { 'Content-Type': 'application/json' } }) .then(function(response){ if(response.ok) return response.json(); }) .then(function(json){ return JSON.stringify(json); }) .catch(error => console.error('Error:', error)); }
// 上传文件方式 function uploadFile(url,formData){ return fetch(url, { method: 'POST', credentials: "include", body: formData }) .then((response) => { return response.text() }) .catch(error => console.error('Error:', error)) .then(response => response); }