1.解析 url 获取键(name)值对 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 function getSearchParam (name ) { var reg = new RegExp ('(^|&)' + name + '=([^&]*)(&|$)' ); var r = window .location.search.substr(1 ).match(reg); if (r != null ) { return decodeURIComponent (r[2 ]); } else { return null ; } } function getUrlParams (url ) { if (typeof url != 'string' ) { return {}; } var query = url.split('#' )[0 ].split('?' )[1 ]; if (!query) { return {}; } var params = {}; var paramStrArr = query.split('&' ); for (var i = 0 ; i < paramStrArr.length; ++i) { var keyValArr = paramStrArr[i].split('=' ); var key = decodeURIComponent (keyValArr[0 ]); var val = keyValArr[1 ] == undefined ? true : decodeURIComponent (keyValArr[1 ]); if (key) { params[key] = val; } } return params; } function queryParams ( ) { var params = {}; decodeURIComponent (location.search) .replace('?' , '' ) .split('&' ) .forEach(function (paramStr ) { var kv = paramStr.split('=' ); params[kv[0 ]] = kv[1 ]; }); return params; } function getUrlParams ( ) { var params = {}; decodeURIComponent (location.search) .substring(1 ) .split('&' ) .forEach(function (paramStr ) { var kv = paramStr.split('=' ); params[kv[0 ]] = kv[1 ]; }); return params; } const getURLParameters = (url ) => url .match(/([^?=&]+)(=([^&]*))/g ) .reduce( (a, v) => ( (a[v.slice(0 , v.indexOf('=' ))] = v.slice(v.indexOf('=' ) + 1 )), a ), {} );
2.merge 实现 1 2 3 4 5 6 7 8 9 10 11 12 function mergeParam (array ) { var newJsonObj = {}; for (var i = 0 ; i < array.length; i++) { var jsonObj = array[i]; if (jsonObj instanceof Object ) { for (var key in jsonObj) { newJsonObj[key] = jsonObj[key]; } } } return newJsonObj; }
3.offsetTop 的稳妥写法 1 2 3 4 5 6 7 8 9 getTop:function (elements ) { var top = elements.offsetTop; var parent = elements.offsetParent; while ( parent != null ){ top += parent.offsetTop; parent = parent.offsetParent; }; return top; }
4.在即将进入屏幕范围的时候开始加载逻辑 1 2 3 4 5 6 7 8 9 10 showimg: function ( ) { var preView = 200 ; if (this .flag) return ; var position = this .getTop(this .setele) ; var h = document .body.scrollTop + window .screen.height; if (position - this .preView <= h && position + this .preView >= document .body.scrollTop) { this .flag = true ; this .setsrc(); }
5.下拉加载更多 步骤: 一、事件触发加载事件
二、判断 1.滚到底部 2.没有加载完成字样 3.loading 图标没有显示
三、初始化 让 loading 显示(display:block) 让 pageNum +=1;
四、ajax
6.移动端系统判断 1 2 3 4 5 6 7 8 9 var deviceType = (function ( ) { return { mobile: !!u.match(/AppleWebKit.*Mobile.*/ ), ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/ ), iPhone: u.indexOf('iPhone' ) > -1 , iPad: u.indexOf('iPad' ) > -1 , android: u.indexOf('Android' ) > -1 , }; })();
7.操作 cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 function getCookies ( ) { var cookies = {}; var all = document .cookie; if (all === '' ) return cookies; var list = all.split('; ' ); for (var i = 0 ; i < list.length; i++) { var cookie = list[i]; var p = cookie.indexOf('=' ); var name = cookie.substring(0 , p); var value = cookie.substring(p + 1 ); value = decodeURIComponent (value); cookies[name] = value; } return cookies; } function getCookie (name ) { var arr, reg = new RegExp ('(^| )' + name + '=([^;]*)(;|$)' ); if ((arr = document .cookie.match(reg))) return unescape (arr[2 ]); else return null ; } function setCookie (name, value ) { var Days = 30 ; var exp = new Date (); exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000 ); document .cookie = name + '=' + escape (value) + ';expires=' + exp.toGMTString(); } if (cookie != '' ) { var cookieArr = cookie.split(';' ); var dd = new Date (); var uid = '' ; dd.setTime(dd.getTime() + 3600 * 24 * 1000 ); if (cookie.indexOf('PPU' ) > -1 ) { uid = cookie.split('PPU=' )[1 ].split(';' )[0 ].split('UID=' )[1 ].split('&' )[0 ]; document .cookie = 'uid=' + uid + ';domain=58.com; path=/; expires=' + dd.toGMTString(); } cookieArr.map(function (item ) { if (!!item) { document .cookie = item + ';domain=58.com; path=/; expires=' + dd.toGMTString(); } }); } function delCookie (name ) { var exp = new Date (); exp.setTime(exp.getTime() - 1 ); var cval = getCookie(name); if (cval != null ) document .cookie = name + '=' + cval + ';expires=' + exp.toGMTString(); } document .cookie.split(';' ).forEach(function (c ) { document .cookie = c .replace(/^ +/ , '' ) .replace(/=.*/ , '=;expires=' + new Date ().toUTCString() + ';path=/' ); }); function clearAllCookie ( ) { var keys = document .cookie.match(/[^ =;]+(?=\=)/g ); if (keys) { for (var i = keys.length; i--; ) document .cookie = keys[i] + '=0;expires=' + new Date (0 ).toUTCString(); } }
8.写死 uid 注意:必须写上 domain 和 path 才行,这样才是对整个域名进行重写 代码内操作:
1 2 3 import Cookies from 'js-cookie' ;Cookies.remove('zz_uid' ); Cookies.set('zz_uid' , '39099770641173' , { domain : '58.com' , path : '/' });
浏览器内操作:
1 2 3 4 5 document .cookie = `uid=52;domain=zhuanzhuan.com;path=/;expires=Sat, 19 May 2118 03:16:29 GMT` ;或者; document .cookie = 'rrtr=3133;domain=192.168.149.14;path=/;expires=' + new Date (Date .now() + 3600 * 24 * 1000 * 3 ).toGMTString();
9.文本选择器 1 2 3 4 5 6 7 8 9 10 var getParents = document .querySelectorAll('.frequentQ' );for (var i = 0 ; i < getParents.length; i++) { if (getParents[i].innerHTML.trim() == '快递费用' ) { var that = getParents[i]; that.addEventListener('click' , function ( ) { alert('调用成功' ); }); } }
10.判断是否有网络 1 2 3 if (!navigator.onLine) { }
还有一种方式是事件:
1 2 3 4 5 6 element.addEventListener(window , 'online' , function ( ) { alert('Online' ); }); element.addEventListener(window , 'offline' , function ( ) { alert('Offline' ); });
11.小数点操作 1 2 3 4 5 6 7 8 9 10 11 12 13 function clearNoNum (obj ) { obj.value = obj.value.replace(/[^\d.]/g , '' ); obj.value = obj.value.replace(/\.{2,}/g , '.' ); obj.value = obj.value .replace('.' , '$#$' ) .replace(/\./g , '' ) .replace('$#$' , '.' ); obj.value = obj.value.replace(/^(\-)*(\d+)\.(\d\d).*$/ , '$1$2.$3' ); if (obj.value.indexOf('.' ) < 0 && obj.value != '' ) { obj.value = parseFloat (obj.value); } }
emoji 转换成字符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 utf16toEntities = (str ) => { var patt = /[\ud800-\udbff][\udc00-\udfff]/g ; str = str.replace(patt, function (char ) { var H, L, code; if (char.length === 2 ) { H = char.charCodeAt(0 ); L = char.charCodeAt(1 ); code = (H - 0xd800 ) * 0x400 + 0x10000 + L - 0xdc00 ; return '&#' + code + ';' ; } else { return char; } }); return str; };
锁屏显示页面触发/焦点在标签页上切换触发事件 1 document .addEventListener('webkitvisibilitychange' , onVisibilityChanged, false );
onpopstate 事件 1 2 3 4 5 6 7 8 9 10 11 12 window .onpopstate = function (event ) { alert( 'location: ' + document .location + ', state: ' + JSON .stringify(event.state) ); }; history.pushState({ page : 1 }, 'title 1' , '?page=1' ); history.pushState({ page : 2 }, 'title 2' , '?page=2' ); history.replaceState({ page : 3 }, 'title 3' , '?page=3' ); history.back(); history.back(); history.go(2 );
这个事件只有单页面路由切换触发,但是真正切换标签页不触发,要用上边的方法https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onpopstate
判断是否支持事件 1 2 3 4 5 var div = document .createElement('div' ), supportTouch = 'ontouchstart' in div, supportOtc = 'onorientationchange' in window ;
判断是否支持样式 1 2 3 4 5 6 7 var element = document .createElement('div' );if (attr in element.style) { element.style[attr] = value; return element.style[attr] === value; } else { return false ; }
url 规范化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function correctHashRoute ( ) { let url = location.href, queryArr = [], queryStr = '' , splitQuery = (str ) => str.substring(1 ).split('&' ), reg = /^(https?\:\/\/[\w\.\/]+)(\?[^#]+)?(#[^\?&]+)?(.+)?$/ , res = reg.exec(url); if (!res || !res[3 ]) return ; if (res[4 ]) queryArr = queryArr.concat(splitQuery(res[4 ])); if (res[2 ]) queryArr = queryArr.concat(splitQuery(res[2 ])); if (!queryArr.length) return ; queryArr = [...new Set (queryArr)]; queryStr = '?' + queryArr.join('&' ); location.hash = res[3 ] + queryStr; }
判断手机号 1 /^1 [34578 ]\d{9 }/g.test(num);
单行写一个评级组件 1 '★★★★★☆☆☆☆☆' .slice(5 - rate, 10 - rate);
用法:
优雅的取随机字符串 1 2 Math .random().toString(16 ).substring(2 ); Math .random().toString(36 ).substring(2 );
金钱格式化 1 2 3 var test1 = '1234567890' ;var format = test1.replace(/\B(?=(\d{3})+(?!\d))/g , ',' );console .log(format);
曾经提问版本
1 2 '12334565632' .replace(/(\d)(?=(\d{3})+$)/g , '$1,' );
实现标准 JSON 的深拷贝 1 2 3 4 5 var a = { a: 1 , b: { c : 1 , d : 2 }, }; var b = JSON .parse(JSON .stringify(a));
不过对于undefined
和function
的会忽略掉
最短数组去重 1 [...new Set ([1 , '1' , 2 , 1 , 1 , 3 ])];
短路表达式 1 2 var a = b && 1 ;var a = b || 1 ;
替换空格和回车 1 2 3 var resultStr = testStr.replace(/\ +/g , '' ); resultStr = testStr.replace(/[ ]/g , '' ); resultStr = testStr.replace(/[\r\n]/g , '' );
时间转换 1 2 3 4 let date = new Date (parseInt (1503487641772 )); this .dealTime = `${date.getFullYear()} -${ date.getMonth() + 1 } -${date.getDate()} ${date.getHours()} :${date.getMinutes()} ` ;
decodeURIComponent 加号不能转换成空格问题 原因: urlencode 将空格转换成+号 rowurlencode 将空格转换成%20
解决办法: 在调用 decodeURIComponent 函数之前要先把+替换为%20
1 decodeURIComponent (str.replace(/\+/g , '%20' ));
解决办法二: 传送之前前端先 encodeURIComponent 一次
vue 转义字符 v-html 可以显示转义字符
等
给数组添加默认值
参考文献
去掉两端双引号 1 a = a.substring(1 , a.length - 1 );
JSONP 库的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import jsonp from 'jsonp' ;export const JSONP = (url, params ) => { let paramArr = []; Object .keys(params).forEach(function (key ) { paramArr.push(key + '=' + params[key]); }); let toStringParam = JSON .stringify(paramArr.join('&' )); let delQuote = toStringParam.substring(1 , toStringParam.length - 1 ); return new Promise ((resolve, reject ) => { jsonp( 'https://zhuan.58.com/zz/transfer/' + url + '?' + delQuote, null , (err, res) => { if (err) { reject(Error (err)); } else if (res) { resolve(res); } } ); }); };
关闭页面发送请求 事件:beforeunload / pagehide 下个说明做了兼容 方法一:通过请求 img
1 2 3 4 window .onbeforeunload = function ( ) { var img = new Image(); img.src = url; };
补充 :onpagehide 存在兼容问题,有些手机不支持,document.querySelector(‘body’)有些机型不能及时出发 方法二:将 ajax 进行同步执行
1 xmlHttpReq.open('GET' , url, false );
参考文献
兼容关闭页面事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (OS_TYPE().isiOS) { window .onpagehide = () => { this .beforeUnloadFun(); }; } else { window .onbeforeunload = () => { this .beforeUnloadFun(); }; } document .addEventListener('visibilitychange' , () => { if (document .visibilityState === 'hidden' ) { this .unLoadLego(); } });
当后台运行时,客户端提供方法
图片等比缩放填充区域 1 2 3 4 <div class ="goods" style ="{{backgroundImage:`url(${util.getHeadImgSize(item.pic)})`}}" > </div >
1 2 3 background-size : cover ;background-repeat : no-repeat ;background-position : center center ;
判断输入字节数 方法一:
1 2 3 4 5 String .prototype.len = function ( ) { return this .replace(/[^\x00-\xff]/g , 'xx' ).length; }; alert('文字abc' .len());
方法二:
1 2 3 4 5 6 7 8 9 10 function chkstrlen (str ) { var strlen = 0 ; for (var i = 0 ; i < str.length; i++) { if (str.charCodeAt(i) > 255 ) strlen += 2 ; else strlen++; } return strlen; }
自己开发的字节限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 dealChAndEn(str,max,endChar){ let strlen = 0 ; let strResult; let realyLen = 0 ; for (var i = 0 ;i < str.length; i++) { if (str.charCodeAt(i) > 255 ){ strlen += 2 ; }else { strlen++; } if (strlen>=max){ realyLen = i; strResult = str.substring(0 ,i+1 ); break ; } } if (strlen<max){ strResult = str } return { strResult: endChar ? strResult + endChar : strResult, realyLen: realyLen, strlen: strlen } }
break,continue,return 区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for (var i = 1 ; i <= 10 ; i++) { if (i == 8 ) { break ; } document .write(i); } for (var i = 1 ; i <= 10 ; i++) { if (i == 8 ) { continue ; } document .write(i); }
渐变兼容模式 1 2 background : linear-gradient (-17deg , #8cd7fe 0%, #a5dafc 100%);background : -webkit-linear-gradient (-17deg , #8cd7fe 0%, #a5dafc 100%);
参考文献
json-server 监听模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 var fs = require ('fs' );var path = require ('path' );var db = {};module .exports = function ( ) { var files = scanDir(path.join(__dirname, './data' )); files.forEach((file ) => { db[path.basename(file).split('.' )[0 ]] = require (file); }); files.forEach((file ) => { fs.watch(file, () => { delete require .cache[path.resolve(file)]; try { db[path.basename(file).split('.' )[0 ]] = require (file); } catch (e) { } }); }); return db; }; function scanDir (dir ) { var files = []; scan(dir); function scan (_dir ) { if (fs.statSync(_dir).isFile() && _dir.indexOf('.DS_Store' ) < 0 ) { return files.push(_dir); } return ( fs.statSync(_dir).isDirectory() && fs.readdirSync(_dir).forEach((file ) => { scan(_dir + '/' + file); }) ); } return files; }
获取文本宽度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 document .getElementById('span' ).onclick = function ( ) { var objRectList = this .getClientRects(), length = objRectList.length; console .log(objRectList); var string = '有' + length + '个矩形\r\n' ; for (var i = 0 ; i < length; i += 1 ) { string += '第' + (i + 1 ) + '个矩形: top:' + objRectList[i].top + ', right:' + objRectList[i].right + ', bottom:' + objRectList[i].bottom + ', left:' + objRectList[i].left + ', width:' + objRectList[i].width + ', height:' + objRectList[i].height + '\r\n' ; } alert(string); };
参考文献 1 参考文献 2
折叠全文库-溢出省略号显示 https://github.com/josephschmitt/Clamp.js/blob/master/clamp.js
1 2 3 4 5 6 $clamp(node, { clamp: 3 , useNativeClamp: false , truncationChar: ' ' , truncationHTML: '<span class="setColor">Read more.这里可以是连接</span>' , });
纯 css 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <html lang ="zh-CN" > <head > <title > 省略号</title > </head > <style > .demo { background : #099 ; max-height: 40px; line-height: 20px; overflow: hidden; } .demo ::before { float: left; content: ''; width: 20px; height: 40px; background: green; } .demo .text { float: right; width: 100%; margin-left: -20px; word-break: break-all; } .demo ::after { float: right; content: '...'; width: 20px; height: 20px; background: red; position: relative; left: 100%; transform: translate(-100%, -100%); } </style > <body > <div class ="demo" > <div class ="text" > 这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本 </div > </div > </body > </html >
可能是最全的 “文本溢出截断省略” 方案合集 用法参考
浮窗被键盘挡住 前提 1 :必须是 absolute,不能是 fixed前提 2 :暂不支持 IOS9.3.5 和 11.1 会出现 bug
1 2 3 4 5 6 7 8 <input @focus ="focusInput" @blur ="blurInput" v-model ="replyContent" type ="text" class ="input" placeholder ="我来帮助TA" />
1 2 3 4 5 focusInput () { this .setIntervalScroll = setInterval(function ( ) { document .body.scrollTop = document .body.scrollHeight; }, 200 ); }
其他人写法
1 2 3 4 setTimeout(() => { window .scrollTo(0 , 1000000 ); this .$refs.chatWrap.scrollTop = 999999 ; }, 500 );
长按复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 div ,form ,html ,li ,p ,span ,ul ,h2 ,h3 ,input { user-select : initial; -webkit-user-select : initial; -webkit-touch-callout : initial; touch-callout : initial; }
推荐还是在要复制的部分加上,否则如果是单页面应用的话,所有页面都可以复制就不太好了
1 2 3 4 .title { user-select : text; -webkit-user-select : text; }
底部浮窗实现方法 方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .wrap { position : absolute; top : 0 ; left : 0 ; right : 0 ; bottom : 0 ; height : 100% ; display : flex; flex-direction : column; .top { flex : 1 ; overflow-y : auto; -webkit-overflow-scrolling : touch; } .bottom { flex : auto; height : 2.56rem ; } }
方法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .wrap { position : absolute; top : 0 ; left : 0 ; right : 0 ; bottom : 0 ; height : 100% ; display : flex; flex-direction : column; .top { top : 0 ; left : 0 ; right : 0 ; bottom : 110px ; position : absolute; overflow-y : auto; -webkit-overflow-scrolling : touch; } .bottom { position : absolute; bottom : 0 ; } }
横向不换行,超出滚动 1 2 3 4 white-space : nowrap;overflow-x : scroll;overflow-y : hidden;-webkit-overflow-scrolling : touch;
bord 和 border 区别 bold 粗体 bolder 比粗体更加粗 lighter 比正常字体淡 100 至少和 200 一样淡 200 至少和 100 一样粗,至少和 300 一样淡 300 至少和 200 一样粗,至少和 400 一样淡 400 字体正常显示,相当于 normal。 500 至少和 400 一样粗,至少和 600 一样淡 600 至少和 500 一样粗,至少和 700 一样淡 700 粗体,相当于 bold。 800 至少和 700 一样粗,至少和 800 一样淡 900 至少和 800 一样粗
移动端控制台 vConsole 1 2 3 4 <script src ="https://wechatfe.github.io/vconsole/lib/vconsole.min.js?v=3.0.0.0" > </script > <script > var vConsole = new VConsole(); </script >
IOS11 底部固定浮动窗不浮动问题 办法一: 当底部按钮为一个 input 输入框的时候,底部浮窗会跟随输入法一起弹起 如果为 div 则不会跟随弹起
办法二: 不可用-因为这个方法是让高度超过 100%的时候,才用到,而 ios11.1 屏幕挡住部分也算在 100%内 通过 scrollIntoView 方法使元素强制显示到窗口内
1 document .querySelector('#result-area' ).scrollIntoView(true );
注意:如果上述方法仍不能实现 可能你用了 scrollTop 类代码~请去掉
判断 IOS 版本 1 2 3 4 5 6 7 8 9 10 11 12 let IOS_V = (a ) => { let v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/ ); if (v) { v = parseFloat (v[1 ] + '.' + v[2 ], 10 ); if (v - a > 0 ) { return 1 ; } else if (v == a) { return 0 ; } return -1 ; } };
反击爬虫,前端工程师的脑洞可以有多大? 1 http://litten.me/2017/07/09/prevent-spiders/
浏览器种 cookie // 没有加域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function setCookie (name, value ) { var Days = 30 ; var exp = new Date (); exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000 ); document .cookie = name + '=' + escape (value) + ';expires=' + exp.toGMTString(); } setCookie('id58' , 'c5/nT1mv37MErFV/BBRRAg==' ); setCookie('t' , '15' ); setCookie('tk' , '1FC7096BB25439E8134C48641EE33734' ); setCookie('v' , '3.6.99' ); setCookie('channelid' , 'unknown' ); setCookie('lat' , '39.987035' ); setCookie('lon' , '116.504727' ); setCookie('osv' , '25' ); setCookie('model' , 'MI+5X' ); setCookie('brand' , 'xiaomi' );
当你给一个元素设置过 position:absolute;或者 position:relative;后再增加-webkit-overflow-scrolling: touch;属性后,你会发现,滑动几次后可滚动区域会卡主,不能在滑动,这时给元素增加个 z-index 值就可以了。
1 2 3 -webkit-overflow-scrolling : touch;position : absolute;z-index : 1 ;
vue-resource 跨域 1 Vue.http.post(API.REPLAY_COMMENT, { params }, { credentials : true });
初始化字体 1 2 3 4 5 6 7 body { font-family : 'Helvetica Neue' , Helvetica, Arial, 'PingFang SC' , 'Hiragino Sans GB' , 'Heiti SC' , 'Microsoft YaHei' , 'WenQuanYi Micro Hei' , sans-serif; -webkit-font-smoothing : antialiased; -moz-osx-font-smoothing : grayscale; }
清除选中文本 1 2 3 4 5 6 7 8 9 10 11 12 if (window .getSelection) { if (window .getSelection().empty) { window .getSelection().empty(); } else if (window .getSelection().removeAllRanges) { window .getSelection().removeAllRanges(); } } else if (document .selection) { document .selection.empty(); }
禁止键盘输入 1 2 3 4 if (event.keyCode == 101 ) { event.returnValue = false ; }
禁用参考文档
禁止切换输入法 有兼容问题
1 <input type ="tel" style ="ime-mode:disabled;" />
时间格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const formatTimes = () => { Date .prototype.Format = function (format ) { var o = { 'M+' : this .getMonth() + 1 , 'd+' : this .getDate(), 'h+' : this .getHours(), 'm+' : this .getMinutes(), 's+' : this .getSeconds(), 'q+' : Math .floor((this .getMonth() + 3 ) / 3 ), S: this .getMilliseconds(), }; if (/(y+)/ .test(format)) { format = format.replace( RegExp .$1 , (this .getFullYear() + '' ).substr(4 - RegExp .$1. length) ); } for (var k in o) { if (new RegExp ('(' + k + ')' ).test(format)) { format = format.replace( RegExp .$1 , RegExp .$1. length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) ); } } return format; }; };
超长字符串转数字 1 2 (1.4000000000000001 ).toPrecision(12 );
保留小数后几位+转万 harmony 1 2 3 if (val > 10000 ) { return (val / 10000 ).toFixed(1 ) + '万' ; }
参考
Vue 出现删除光标前有空格 Bug computed 造成的,去掉 computed 属性
Array.from 应用 harmony 1 Array .from(arrayLike, mapFn, thisArg);
arrayLike 想要转换成数组的伪数组对象或可迭代对象。 mapFn (可选参数) 如果指定了该参数,新数组中的每个元素会执行该回调函数。 thisArg (可选参数) 可选参数,执行回调函数 mapFn 时 this 对象。
harmony 1 2 3 4 5 6 Array .from([1 , 2 , 3 ], (x) => x + x);Array .from({ length : 5 }, (v, i) => i);
Array.prototype.reduce() harmony 1 arr.reduce([callback, initialValue]);
callback 执行数组中每个值的函数,包含四个参数: previousValue 上一次调用回调函数返回的值,或者是提供的初始值(initialValue) currentValue 数组中当前被处理的元素 currentIndex 当前被处理元素在数组中的索引, 即 currentValue 的索引.如果有 initialValue 初始值, 从 0 开始.如果没有从 1 开始. array 调用 reduce 的数组 initialValue 可选参数, 作为第一次调用 callback 的第一个参数。
harmony 1 2 3 [0 , 1 , 2 , 3 , 4 ].reduce((previousValue, currentValue, currentIndex, array ) => { return previousValue + currentValue; }, 10 );
getBoundingClientRect 获取盒子的坐标
style、getComputedStyle style
只能获取行间样式,但能设置样式。
getComputedStyle
和 currentStyle
能够获取 行间样式/非行间样式/浏览器默认样式,但存在浏览器兼容问题,且不能设置样式
分组正则 harmony 1 2 url.match(/([^?=&]+)(=([^&]*))/g );
这个是从做到右看
匹配到
1 ["http://url.com/page", "name", "Adam", "surname", "Smith", "aa", "bb"]
然后又会让上述这些匹配到的 进行向后 匹配
会匹配到
1 ["name=", "surname=", "aa=", "bb="]
如果后边再加上([^&]*)内容的话就会匹配的更多
1 ["name=Adam", "surname=Smith", "aa=", "bb="]
字符串乱序 harmony 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const anagrams = (str ) => { if (str.length <= 2 ) return str.length === 2 ? [str, str[1 ] + str[0 ]] : [str]; return str .split('' ) .reduce( (acc, letter, i) => acc.concat( anagrams(str.slice(0 , i) + str.slice(i + 1 )).map( (val) => letter + val ) ), [] ); };
数字精度处理 harmony 1 2 num.toPrecision('1~21之间' ); num.toFixed('0~20之间' );
Big 插件 JavaScript 浮点数陷阱及解法
精度丢失原因 计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926…,1.3333… 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。如图 意义: 1 位用来表示符号位 11 位用来表示指数 52 位表示尾数
浮点数转化成二进制出现无限循环的情况,而 52 位尾数不够用,出现舍入问题,造成精度丢失 例如:
0.1 >> 0.0001 1001 1001 1001…(1001 无限循环) 0.2 >> 0.0011 0011 0011 0011…(0011 无限循环)
此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
以上,可以知道看似有穷的数字, 在计算机的二进制表示里却是无穷的,由于存储位数限制因此存在“舍去”,精度丢失就发生了。
浏览器内核 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var browser = { versions: (function ( ) { var u = navigator.userAgent, app = navigator.appVersion; return { trident: u.indexOf('Trident' ) > -1 , presto: u.indexOf('Presto' ) > -1 , webKit: u.indexOf('AppleWebKit' ) > -1 , gecko: u.indexOf('Gecko' ) > -1 && u.indexOf('KHTML' ) == -1 , mobile: !!u.match(/AppleWebKit.*Mobile.*/ ), ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/ ), android: u.indexOf('Android' ) > -1 || u.indexOf('Adr' ) > -1 , iPhone: u.indexOf('iPhone' ) > -1 , iPad: u.indexOf('iPad' ) > -1 , webApp: u.indexOf('Safari' ) == -1 , weixin: u.indexOf('MicroMessenger' ) > -1 , qq: u.match(/\sQQ/i ) == ' qq' , }; })(), language: (navigator.browserLanguage || navigator.language).toLowerCase(), };
svg 或 canvas 保存为 png HTMLCanvasElement.toDataURL() d3-downloadable
tr td 无法设置边框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 方法1: table{ border-collapse: collapse; } tr{ border-bottom: 1px solid red; } 解释,border-collapse: collapse; 将table,th和td的边框合成单一的边框,此时在使用tr就可以达到目的。 方法2: 先在table标签里面设置cellspacing等于0, <table cellspacing="0"> td{ border-bottom: 1px solid red; } 解释,如何不设置cellspacing等于0,直接设置td的下边框,则下边框会不连续,因为cellspacing不为零,单元格于单元格之间存在间隙。
img 灰色默认外边框的去除 全局样式
1 2 3 4 img [src=''] ,img :not( [src] ) { opacity : 0 ; }
img 灰色默认外边框的去除
授权接口 1 https://xxxxx.xx.com/activity/comm/shouQuan?callbackUrl=https%3A%2F%2Fm.zhuanzhuan.58.com%2FMzhuanzhuan%2Fwechat%2Fzzhelp%2Fguide.html
授权接口和临时票据不一样
location.origin 兼容 1 window .location.origin = window .location.protocol + "//" + window .location.hostname + (window .location.port ? ':' + window .location.port: ''
返回扩展名 1 2 'emoji.pnn.png' .match(/\.[^.]*/g );
图片获取失败 1 2 3 4 5 function nofind ( ) { var img = event.srcElement; img.src = 'images/logoError.png' ; img.onerror = null ; }
1 <img src ="images/logo.png" onerror ="nofind();" />
CSS 写自适应大小的正方形 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <style type ="text/css" > .img { width: 100%; height: 0; padding-bottom : 100%; overflow: hidden; background: url(../res/images/haha.png) center/100% 100% no-repeat; } .img img { width: 100%; } .img { position: relative; width: 100%; height: 0; padding-bottom : 100%; overflow: hidden; } .img img { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } </style > <div class ="img" > </div >
不定期更新的 CSS 奇淫技巧
版本号比较 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function toNum (a ) { var a = a.toString(); var c = a.split('.' ); var num_place = ['' , '0' , '00' , '000' , '0000' ], r = num_place.reverse(); for (var i = 0 ; i < c.length; i++) { var len = c[i].length; c[i] = r[len] + c[i]; } var res = c.join('' ); return res; } function cpr_version (a, b ) { var _a = toNum(a), _b = toNum(b); if (_a == _b) console .log('版本号相同!版本号为:' + a); if (_a > _b) console .log('版本号' + a + '是新版本!' ); if (_a < _b) console .log('版本号' + b + '是新版本!' ); } var a = '2.2.3' ;b = '2.1.15' ; cpr_version(a, b);
visibilitychange 事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var hidden, visibilityChange;if (typeof document .hidden !== 'undefined' ) { hidden = 'hidden' ; visibilityChange = 'visibilitychange' ; } else if (typeof document .mozHidden !== 'undefined' ) { hidden = 'mozHidden' ; visibilityChange = 'mozvisibilitychange' ; } else if (typeof document .msHidden !== 'undefined' ) { hidden = 'msHidden' ; visibilityChange = 'msvisibilitychange' ; } else if (typeof document .webkitHidden !== 'undefined' ) { hidden = 'webkitHidden' ; visibilityChange = 'webkitvisibilitychange' ; } document .addEventListener( visibilityChange, function ( ) { console .log('当前页面是否被隐藏:' + document [hidden]); }, false );
捕捉错误 1 2 3 window .onerror = function (message, url, line, column, error ) { console .log('log---onerror::::' , message, url, line, column, error); };
box-shadow 1 2 3 .box-shadow-ps (@a ,@b ,@c ,@d ,@e ) { box-shadow : (@c *cos (180 -@b )) (@c *sin (180 -@b )) (@e - @d * @e ) (@d * @e ) @a ; }
正则切分 url 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let url = location.href, queryArr = [], queryStr = '' , splitQuery = (str ) => str.substring(1 ).split('&' ), reg = /^(https?\:\/\/[\w\.\/]+)(\?[^#]+)?(#[^\?&]+)?(.+)?$/ , res = reg.exec(url); if (!res || !res[3 ]) return ;if (res[4 ]) queryArr = queryArr.concat(splitQuery(res[4 ]));if (res[2 ]) queryArr = queryArr.concat(splitQuery(res[2 ]));if (!queryArr.length) return ;queryArr = [...new Set (queryArr)]; queryStr = '?' + queryArr.join('&' ); location.hash = res[3 ] + queryStr;
更好的正则匹配 url 1 2 3 /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/.exec( location.href );
宏任务微任务 先同步 → 微任务 → 宏任务
宏任务: setTimeout setInterVal 微任务: Promise 的 then 内的任务
1 2 3 4 5 6 7 8 9 10 11 setTimeout(function ( ) { console .log('4' ); }, 10 ); new Promise (function (resolve ) { console .log('5' ); setTimeout(resolve, 3000 ); }).then(function ( ) { console .log('6' ); });
Node 下: 宏任务放在一起执行算一个队列
时间转换 1 2 3 new Date (2009 , 1 , 1 ); new Date ('2009/1/1 10:00:00' );
简单深拷贝 1 2 var a = {}; var b = JSON .parse(JSON .stringify(a));
删除 node_modules
animate 不动问题 1 2 3 4 5 6 7 8 9 10 11 12 .active { -webkit-animation : spring 0.6s linear infinite; } @-webkit-keyframes spring { from { transform : rotateY(0 ); } to { transform : rotateY(360deg ); } }
注意:不能含有不带-webkit 的样式,否则也不生效
对象排序 1 2 3 4 5 6 7 8 9 10 11 12 13 var arr = [ { name : 'abc' , age : 20 }, { name : 'Cde' , age : 19 }, { name : 'dfc' , age : 25 }, { name : 'Bde' , age : 21 }, ]; arr.sort(function (a, b ) { var s = a.name.toLowerCase(); var t = b.name.toLowerCase(); if (s < t) return -1 ; if (s > t) return 1 ; }); console .log(arr);
终止 forEach 语句 1 2 3 try { throw new Error ('抛错来终止' ); } catch (e) {}
通过 every 或 some 来替代
logN 如果 a 的 x 次方等于 N(a>0,且 a 不等于 1),那么数 x 叫做以 a 为底 N 的对数(logarithm),记作 x=logaN。
跳转到 ios 应用商店 1 2 3 "https://itunes.apple.com/us/app/apple-store/id_MY_APP_ID" 或 "itms-apps://itunes.apple.com/us/app/apple-store/id_MY_APP_ID"
本地城市按拼音排序 1 2 3 4 var list = ['王' , '张' , '李' ];list.sort(function (a, b ) { return a.localeCompare(b, 'zh' ); });
获取路径区分 1 2 3 4 5 6 7 8 9 10 11 const path = require ('path' )console .log('__dirname:' , __dirname)console .log('__filename:' , __filename)console .log('process.cwd():' , process.cwd())console .log('./:' , path.resolve('./' ))__dirname: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs __filename: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs/2. path.js process.cwd(): /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs ./: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
总结:dirname: 获得当前执行文件所在目录的完整目录名
filename: 获得当前执行文件的带有完整绝对路径的文件名 process.cwd():获得当前执行 node 命令时候的文件夹目录名 ./: 不使用 require 时候,./与 process.cwd()一样,使用 require 时候,与__dirname 一样
引入文件夹内文件 1 2 3 4 const requireAll = (requireContext ) => requireContext.keys().map(requireContext); const req = require .context('@/assets/img/icons/svg' , false , /\.svg$/);const iconMap = requireAll(req);
webpack 中 require.context 的使用
去掉 whistle 的 https 安全问题 window: chrome 的快捷方式-属性-快捷方式-目标 后面加上
1 (空格)--disable-infobars --ignore-certificate-errors
访问 https 的资源就不会出现安全拦截了
Mac:https://www.jianshu.com/p/a8cc2c04ee7c
浮层禁止底部滑动 PC 端:
1 overscroll-behavior : contain ;
复制内容到剪贴板 复制内容到剪贴板
clipboard.js 兼容 ios 1 2 3 4 5 6 7 8 9 10 11 12 13 <span id ="foo" data-clipboard-target ="#foo" data-clipboard-action ="copy" > 哈哈,我被复制了</span > 改为 <span style ="cursor: pointer" onclick ="" class ="btn" id ="foo" data-clipboard-target ="#foo" data-clipboard-action ="copy" > 哈哈,这下ios可以复制我了</span >
给 span 加了一个 css 属性:cuosor:pointer;以及 onclick=””,这个空事件,原因是 ios 默认非点击标签不具有点击效果,所以给这些标签添加相关属性,这样系统可以识别出来!
参考文献
页面内按需加载 1 2 const privacyAgreement = () => require .ensure([], () => require ('./privacyAgreement' ));
1 2 3 require (['./my-async-component' ], resolve)'text-list' : () => import ('../../../components/common/text-list/index' )
穿透 scope stylus 的样式穿透 使用>>>
1 2 3 4 5 外层 >>> 第三方组件 样式 .wrapper >>> .swiper-pagination-bullet-active background: #fff
sass 和 less 的样式穿透 使用/deep/
1 2 3 4 5 6 外层 /deep/ 第三方组件 { 样式 } .wrapper /deep/ .swiper-pagination-bullet-active{ background: #fff; }
同路由跳转改变参数视图不刷新 https://blog.csdn.net/cmyh100/article/details/79388186 https://my.oschina.net/u/3970421/blog/2995258
1 <router-view v-if ="$route.meta.noAlive" :key="$route.fullPath" ></router-view >
兼容打开和关闭浏览器页面 1 2 3 4 5 6 Vue.prototype.$close = () => { window .opener = null window .open('' , '_self' ) window .close() } Vue.prototype.$open = (obj ) => { const { path = '' , query = {}} = obj let queryString = '' for (var item in query) { if (query.hasOwnProperty(item)) { queryString = `${queryString} ${queryString ? '&' : '' } ${item} =${query[item]} ` } }window .open(`//gm.zhuanzhuan.com/qc${path} ?${queryString} ` ) }
解决取不到值的插件-安全值 @babel/plugin-proposal-optional-chaining
函数解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function safeNestValue (origin, keyStr, defaultValue ) { var result; try { result = eval ('origin.' + keyStr); } catch (e) {} return result === void 0 ? defaultValue : result; } var a = { b: { c: { d: { e: [ 1 , 2 , { f: 'g' , }, ], }, }, }, }; safeNestValue(a, 'b.c.d.e.h' , 999 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var safeNestValue = (origin, keyStr, defaultValue ) => { let keyArr = keyStr.split('.' ).reduce((t, c ) => { return t.concat( c.split(/(\[|\])/ ).filter((item ) => item && item !== '[' && item !== ']' ) ); }, []); const fn2 = (o, arr ) => { if (arr.length === 0 ) return o; const key = arr.splice(0 , 1 ); const result = o[key]; if (result === void 0 ) { return defaultValue; } return fn2(result, arr); }; return fn2(origin, keyArr); };
无报错链式取值的几种方法
XXS 1 login?redirect=javascript%3 Aalert%28 document .cookie%29
1 javascript: alert(document .cookie);
js 去除数组中的空值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 方法一: var u=[undefined ,undefined ,1 ,'' ,'false' ,false ,true ,null ,'null' ];u.filter(d => d); 方法二: Array .prototype.clean = function (deleteValue ) { for (var i = 0 ; i < this .length; i++) { if (this [i] == deleteValue) { this .splice(i, 1 ); i--; } } return this ; }; 方法三: https:
多维数组扁平化 多维数组扁平化 5 种方法实现数组扁平化
1 2 3 4 5 function flatten (arr ) { return arr.reduce((result, item ) => { return result.concat(Array .isArray(item) ? flatten(item) : item); }, []); }
json-server 详细说明 json-server 详解 https://www.jianshu.com/p/5bb86a770e23 json-server 常用自定义路由和简单配置
静态方法和私有属性 ES6 class 静态属性和私有方法 Es6 类 class 的关键 super、static、constructor、new.target
判断一个对象是否为空 1 2 3 4 5 6 7 8 function isEmptyObject (obj ) { return Object .keys(obj).length === 0 ; } function isEmptyObject (obj ) { return JSON .stringify(data) === '{}' ; }
js 判断一个 object 对象是否为空
vue watch 的 deep 应用 Vue.js 中 watch 的高级用法
任意文件转 base64 https://www.zhangxinxu.com/sp/base64.html
字体压缩裁剪 字体子集裁剪 字体压缩 fontmin
字符串转变量名 在 Javascript 中将字符串转换为变量名称 Javascript 中字符串转为变量名的 4 种解决方案
window.onload 关于 JS 的–window.onload()方法
forEach async await 一边用 for…of 来遍历当 async/await 遇上 forEach
禁止蒙层滚动 禁止蒙层底部页面跟随滚动
sticky 不生效 1、sticky 不会触发 BFC。如果不知道 BFC 可以看这里。 2、样式表 z-index 无效。行内 style 写有效。 3、sticky 是容器相关的,也就说 sticky 的特性只会在他所处的容器里生效。 4、元素最近的祖先元素 overflow 设置为非默认值 visible 时,则元素相对于该祖先元素进行 sticky 定位。若最近的祖先元素设置为 overflow:hidden,则元素不会 sticky 定位position: sticky 详解(防坑指南) position:sticky 的兼容性尝试
禁用橡皮筋 方法一:css overscroll-behavior: none
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const delBungee = () => { var startY, endY; document .querySelector('body' ).addEventListener( 'touchstart' , function (e ) { startY = e.touches[0 ].pageY; }, { passive : false } ); document .querySelector('body' ).addEventListener( 'touchmove' , function (e ) { endY = e.touches[0 ].pageY; if ( ((endY > startY || startY === 'undefined' ) && window .scrollY <= 0 ) || window .scrollY === 0 ) { e.preventDefault(); } if ( endY < startY && window .scrollY + document .body.clientHeight >= document .querySelector('body' ).scrollHeight ) { e.preventDefault(); } }, { passive : false } ); };
移动端 iOS 阻止橡皮筋效果 iOS safari 如何阻止“橡皮筋效果”?
拦截监听事件
1 2 3 4 5 6 7 8 9 10 (function ( ) { Element.prototype._addEventListener = Element.prototype.addEventListener; Element.prototype.addEventListener = function (a, b, c ) { this ._addEventListener(a, b, c); if (!this .eventListenerList) this .eventListenerList = {}; if (!this .eventListenerList[a]) this .eventListenerList[a] = []; this .eventListenerList[a].push(b); }; })();
判断当前节点和另外一个节点位置 compareDocumentPosition 获取索引值高效的获取当前元素是父元素的第几个子元素
1 2 3 4 5 6 7 8 getNodeIndex = (ele ) => { var i = 0 ; while (ele.previousSibling) { ele = ele.previousSibling; i++; } return i; };
自定义事件+监听 replaceState 和 pushState 行为 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var _wr = function (type ) { var orig = history[type]; return function ( ) { var rv = orig.apply(this , arguments ); var e = new Event(type); e.arguments = arguments ; window .dispatchEvent(e); return rv; }; }; history.pushState = _wr('pushState' ); history.replaceState = _wr('replaceState' ); window .addEventListener('replaceState' , function (e ) { console .log('THEY DID IT AGAIN! replaceState 111111' ); }); window .addEventListener('pushState' , function (e ) { console .log('THEY DID IT AGAIN! pushState 2222222' ); });
如何监听 URL 的变化?
优化滚动事件 passive 优化滚动事件
判断两个 DOM 是否相同
怎么判断某个 dom 节点是否包含某个 dom 节点?
两个新监听 API IntersectionObserver更优雅的实现元素是否在 viewport 监听(新 api) MutationObserver深入 MutationObserver 补充: getBoundingClientRect 可以检测距离视窗距离,来实现懒加载,但是每次出现到视窗都会触发,所以需要再兼容一次
MessageChannel 和 web worker MessageChannel 是什么,怎么使用?
postMessage 其中分为 window.postMessage() – 可用于 iframe 中通信, 还有 MessageChannel 中的 MC 实例.postMessage() 还有 web worker 实例中的 worker 实例.postMessage()
0.5px 1 2 3 4 5 6 7 8 9 10 meta.setAttribute( 'content' , 'initial-scale=' + 1 / dpr + ', maximum-scale=' + 1 / dpr + ', minimum-scale=' + 1 / dpr + ', user-scalable=no' );
iframe 嵌入 html iframe 展示特定 html 代码 iframe 动态添加 html
利用 history 来监听返回键 vue 实现微信浏览器左上角返回按钮拦截功能 vue 的 router.back 用的是 history.go 实现的
node-sass 安装不上 1 2 3 4 5 6 7 // linux、mac 下 SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass // window 下 set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ && npm install node-sassset SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ ; npm install node-sassnpm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
[node 安装 node-sass 失败,配置淘宝源]https://blog.csdn.net/a295277302/article/details/70992404 为什么 node-sass 总是安装失败?
###用户调整手机字体大小
移动端 rem 布局,用户调整手机字体大小或浏览器字体大小后导致页面布局出错问题
微前端 qiankun(乾坤)
清除微信 cookie debugx5.qq.com
前端 input file 图片压缩
canvas 转 blob base64 转 blob https://blog.csdn.net/weixin_44186072/article/details/95941972
https://www.cnblogs.com/ihuangjianxin/p/9405107.html
https://www.cnblogs.com/teemor/p/12580510.html
base64 转 blob
Observers JS 中的观察者们 —— 四种 Observers
自动化测试 cypress
RxJS https://github.com/tc39/proposal-observable#observable https://rxmarbles.com/ https://thinkrx.io/
SameSite=None
and Secure
解决方案 1.地址栏:chrome://flags 2.搜索:SameSite by default cookies 3.选择:disabledhttps://blog.csdn.net/u014636209/article/details/105198731
修改原型 defineGetter 和 defineSetter
回车搜索 input[type=search] onsearch 事件
HTML5 去除 input [type=search] 的默认边框和删除按钮
vue 动画 vue-transition 动画
清理定时器 1 2 3 4 5 6 7 8 const timer = setInterval(() => { console .log('1' ); }, 1000 ); this .$once('hook:beforeDestory' , () => { clearInterval(timer); timer = null ; });
清理定时器
await 错误处理 方法一: try catch 方法二: 封装函数
1 2 3 const awaitWrap = (promise ) => { return promise.then((data ) => [null , data]).catch((err ) => [err, null ]); };
async/await 的错误处理方法总结与优化
content-visibility content-visibility: 一个可以提高渲染性能的 css 属性
无固定高度没有动画 用 max-height 来代替 初始值设置 max-height: 36px; js 控制 max-height: 1000px;
v-else 闪屏 1 2 3 4 5 <div v-else v-cloak > Not A/B/C</div > [v-cloak] { display:none; }
获取xpath 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var getPath = function (elem ) { if (elem.id != '' ){ return '//*[@id=\"' +elem.id+'\"]' ; } if (elem == document .body){ return '/html/' +elem.tagName.toLowerCase(); } var index = 1 ,siblings = elem.parentNode.childNodes; for (var i = 0 ,len = siblings.length;i<len;i++){ var sibling = siblings[i]; if (sibling == elem){ return arguments .callee(elem.parentNode)+'/' +elem.tagName.toLowerCase()+'[' +(index)+']' ; }else if (sibling.nodeType==1 &&sibling.tagName == elem.tagName){ index++; } } }
方法二:前端全(无)埋点之页面操作路径可视化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function ellocate (el ) { var locator = { xpath : '' , css : '' }; var eloc = { getClass: function (el ) { var formatClass = '' ; var elementClass = el.className; if (typeof elementClass != 'undefined' && elementClass != '' ){ formatClass = '.' + elementClass.split(/[\s\n]+/ ).join('.' ); } return formatClass; }, index: function (el ) { var elements = el.parentNode.children; for (var i=0 ;i<elements.length;i++) { if ( elements[i] == el){ return i; } } } }; for (; el && el.nodeType == 1 ; el = el.parentNode) { var idx = eloc.index(el); if (el.tagName.substring(0 ,1 ) != "/" && el.tagName.toLowerCase() !== 'body' && el.tagName.toLowerCase() !== 'html' ) { if (el.id != 'undefined' && el.id !='' ) { var idPath="[@id=" + "'" + el.id + "'" + "]" ; locator.xpath = '/' + el.tagName.toLowerCase() + idPath + locator.xpath; locator.css = el.tagName.toLowerCase() + '#' + el.id + ' > ' + locator.css; } else { idx='[' + idx + ']' ; locator.xpath = '/' + el.tagName.toLowerCase() + idx + locator.xpath; locator.css = el.tagName.toLowerCase() + eloc.getClass(el) + ' > ' + locator.css; } } } locator.xpath = '//html[1]/body[1]/' + locator.xpath; locator.css = locator.css.substr(0 , locator.css.length-3 ); return locator; };
其他方法
字符串转函数 方法一: eval 方法二:setTimeout(..)
和 setInterval(..)
的第一个参数和eval类似 方法三: do {} es7的语法 方法四: new Function
参考文献:https://blog.csdn.net/daihaoxin/article/details/106213871 https://mp.weixin.qq.com/s/zpg-s0mVWfya6gXx73AVjQ
手写 lodash/get、lodash/set 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function get(object, path, defaultValue) { // 判断 object 是否是数组或者对象,否则直接返回默认值 defaultValue if (typeof object !== 'object') return defaultValue // 沿着路径寻找到对应的值,未找到则返回默认值 defaultValue return _basePath(path).reduce((o, k) => (o || {})[k], object) || defaultValue } // 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用 function _basePath(path) { // 若是数组,则直接返回 if (Array.isArray(path)) return path // 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']' return path.replace(/\[/g, '.').replace(/\]/g, '').split('.') } function set(object, path, value) { if (typeof object !== 'object') return object; _basePath(path).reduce((o, k, i, _) => { if (i === _.length - 1) { // 若遍历结束直接赋值 o[k] = value return null } else if (k in o) { // 若存在对应路径,则返回找到的对象,进行下一次遍历 return o[k] } else { // 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象 o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {} return o[k] } }, object) // 返回object return object; }
参考文献
antd 取/设置表单 1 2 3 4 5 6 const currSkuId = _form.getFieldValue([rowKey || '' , 'skuId' ]) _form.setFields([{name :[rowKey || '' , 'goodsName' ], value : '123' }]) form.setFieldsValue({ content : { pkgList : pkgList } })
监听校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <ProFormText label={`APPID-${index} ` } name="appId" placeholder={'签约商户号绑定的APPID' } rules={[ { validator: (_, value ) => { if (!value || value.trim() === '' ) { return Promise .reject(new Error ('请输入APPID' )) } const { channelAppConfigsList } = formRef.current.getFieldsValue() let errMsg = '' channelAppConfigsList.some((item: any, i: any ) => { if (i != index && item.appId == value) { errMsg = item return true } return false }) if (errMsg) return Promise .reject(new Error ('有重复APPID' )) return Promise .resolve() }, required: true } ]} />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 <Form form={form} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off" > <Form.Item name="area" label="Area" rules={[{ required : true , message : 'Missing area' }]}> <Select options={areas} onChange={handleChange} /> </Form.Item> <Form.List name="sights" initialValue={[ { test: [ { price: '', name: '', }, { price: '', name: '', }, ], }, ]} > {(fields, { add, remove }) => ( <> {fields.map((field) => ( <Space key={field.key} align="baseline"> <Form.List {...field} label="父" name={[field.name, 'test']} fieldKey={[field.fieldKey, 'test']} initialValue={[ { price: '', }, { name: '', }, ]} > {(testList, { add: addtest, remove: removetest }) => ( <> {testList.map((testItem) => { console.log(testItem); return ( <Space key={testItem.key} align="baseline"> <Form.Item {...testItem} label="Price" name={[testItem.name, 'price']} fieldKey={[testItem.fieldKey, 'price']} rules={[{ required: true, message: 'Missing price' }]} > <Input / > </Form.Item> <Form.Item {...testItem} label="Name" name={[testItem.name, 'name']} fieldKey={[testItem.fieldKey, 'name']} rules={[{ required: true, message: 'Missing name' }]} > <Input / > </Form.Item> <MinusCircleOutlined onClick={() => removetest(testItem.name)} / > </Space> ); })} <Form.Item> <Button type="dashed" onClick={() => addtest()} block icon={<PlusOutlined / >} > 填加子 </Button> </ Form.Item> </> )} </ Form.List> <MinusCircleOutlined onClick={() => remove(field.name)} /> </Space> ))} <Form.Item> <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined / >}> 添加外层 </Button> </ Form.Item> </> )} </ Form.List> <Form.Item> <Button type="primary" htmlType="submit" > Submit </Button> </ Form.Item> </Form>
Form.list嵌套Form.list