一、 开发工具类方法
const getType = (obj) => {
const class2type = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regexp',
'[object Object]': 'object',
'[object Error]': 'error',
'[object Symbol]': 'symbol'
}
if (obj == null) {
return obj + ''
}
const str = Object.prototype.toString.call(obj)
console.log('str', str)
console.log('typeof obj', typeof obj)
return typeof obj === 'object' || typeof obj === 'function' ? class2type[str] || 'object' : typeof obj
}
const getUrlQuery = (queryStr) => {
const [query,] = queryStr.split('?')[1].split('#')
if (query) {
return query.split('&').reduce((pre, cur) => {
const [key, val] = cur.split('=')
pre[key] = decodeURIComponent(val)
return pre
}, {})
}
return {}
}
const addStrings = function (num1, num2) {
if (num1 === num2 && num1 === "0") {
return num1
}
num1 = num1.split("").reverse();
num2 = num2.split("").reverse();
const len = Math.max(num1.length, num2.length);
let flag = 0;
const result = [];
for (let i = 0; i < len; i++) {
const n1 = +num1[i] || 0;
const n2 = +num2[i] || 0;
let sum = n1 + n2 + flag;
flag = 0;
if (sum > 9) {
sum -= 10
flag = 1
}
result.push(sum);
}
if (flag) {
result.push(flag)
}
return result.reverse().join("");
}
const reverseWords = function (s) {
return s.trim().split(/\s+/).reverse().join(' ');
};
const binarySearch = function (nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((right - left) / 2) + left;
const num = nums[mid];
if (num === target) {
return mid;
} else if (num > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
const intersection = function (nums1, nums2) {
const set_intersection = (set1, set2) => {
if (set1.size > set2.size) {
return set_intersection(set2, set1);
}
const intersection = new Set();
for (const num of set1) {
if (set2.has(num)) {
intersection.add(num);
}
}
return [...intersection];
}
const set1 = new Set(nums1);
const set2 = new Set(nums2);
return set_intersection(set1, set2);
};
const twoSumIndex = (nums, target) => {
const prevNums = {};
for (let i = 0; i < nums.length; i++) {
const curNum = nums[i];
const targetNum = target - curNum;
const targetNumIndex = prevNums[targetNum];
if (targetNumIndex !== undefined) {
return [targetNumIndex, i];
} else {
prevNums[curNum] = i;
}
}
}
const calculate = function (s) {
const ops = [1];
let sign = 1;
let ret = 0;
const n = s.length;
let i = 0;
while (i < n) {
if (s[i] === ' ') {
i++;
} else if (s[i] === '+') {
sign = ops[ops.length - 1];
i++;
} else if (s[i] === '-') {
sign = -ops[ops.length - 1];
i++;
} else if (s[i] === '(') {
ops.push(sign);
i++;
} else if (s[i] === ')') {
ops.pop();
i++;
} else {
let num = 0;
while (i < n && !(isNaN(Number(s[i]))) && s[i] !== ' ') {
num = num * 10 + s[i].charCodeAt() - '0'.charCodeAt();
i++;
}
ret += sign * num;
}
}
return ret;
}
const validIPAddress = (queryIP) => {
return queryIP.match(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)($|(?!\.$)\.)){4}$/) ? "IPv4" :
queryIP.match(/^(([\da-fA-F]{1,4})($|(?!:$):)){8}$/) ? "IPv6" : "Neither";
}
const filterByID = (item) => {
if (Number.isFinite(item.id) && item.id !== 0) {
return true;
}
return false;
}
const unicodeLength = (str) => {
return Array.from(str).length
}
const deepClone = (obj, hash = new WeakMap()) => {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object") return obj;
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
const isMobile = () => {
if (/Mobi|Android|iPhone/i.test(navigator.userAgent)) {
return true
}
return false
}
const getGlobal = () => {
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
throw new Error('无法找到全局对象')
}
const mergeSort = (arr) => {
let array = mergeSortRec(arr)
return array
}
const mergeSortRec = (arr) => {
let lenth = arr.length
if (lenth == 1) {
return arr
}
let mid = Math.floor(lenth / 2)
let left = arr.slice(0, mid)
let right = arr.slice(mid, lenth)
return merge(mergeSortRec(left), mergeSortRec(right))
}
const merge = (left, right) => {
let result = [],
ileft = 0,
iright = 0
while (ileft < left.length && iright < right.length) {
if (left[ileft] < right[iright]) {
result.push(left[ileft++])
} else {
result.push(right[iright++])
}
}
while (ileft < left.length) {
result.push(left[ileft++])
}
while (iright < right.length) {
result.push(right[iright++])
}
return result
}
const newArray = (arr) => {
while (arr.some(Array.isArray)) {
arr = [].concat(...arr)
}
arr = [...new Set(arr)].sort((a, b) => a - b)
return arr
}
const add = (...args) => {
const _add = (...args1) => {
return add(...args, ...args1)
}
_add.value = () => args.reduce((t, e) => t + e)
return _add
}
const sortArray = (nums) => {
quickSort(0, nums.length - 1, nums);
return nums;
}
const quickSort = (start, end, arr) => {
if (start < end) {
const mid = sort(start, end, arr);
quickSort(start, mid - 1, arr);
quickSort(mid + 1, end, arr);
}
}
const sort = (start, end, arr) => {
const base = arr[start];
let left = start;
let right = end;
while (left !== right) {
while (arr[right] >= base && right > left) {
right--;
}
arr[left] = arr[right];
while (arr[left] <= base && right > left) {
left++;
}
arr[right] = arr[left];
}
arr[left] = base;
return left;
}
class Scheduler {
constructor() {
this.waitTasks = [];
this.excutingTasks = [];
this.maxExcutingNum = 2;
}
add(promiseMaker) {
if (this.excutingTasks.length < this.maxExcutingNum) {
this.run(promiseMaker);
} else {
this.waitTasks.push(promiseMaker);
}
}
run(promiseMaker) {
const len = this.excutingTasks.push(promiseMaker);
const index = len - 1;
promiseMaker().then(() => {
this.excutingTasks.splice(index, 1);
if (this.waitTasks.length > 0) {
this.run(this.waitTasks.shift());
}
});
}
}
class PromiseQueue{
constructor(tasks,concurrentCount=1){
this.totals = tasks.length;
this.todo =tasks;
this.count = concurrentCount;
this.running =[];
this.complete =[];
}
runNext(){
return (
this.running.length < this.count
&& this.todo.length
)
}
run(){
while(this.runNext()){
let promise = this.todo.shift();
promise.then(()=>{
this.complete.push(this.running.shift());
this.run();
})
this.running.push(promise)
}
}
}
const toDecimal = (x) => {
var f = parseFloat(x);
if (isNaN(f)) {
return;
}
f = Math.round(x * 100) / 100;
return f;
}
const toDecimal2 = (x) => {
var f = parseFloat(x);
if (isNaN(f)) {
return false;
}
var f = Math.round(x * 100) / 100;
var s = f.toString();
var rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + 2) {
s += '0';
}
return s;
}
const fomatFloat = (src, pos) => {
return Math.round(src * Math.pow(10, pos)) / Math.pow(10, pos);
}
const accAdd = (arg1, arg2) => {
var r1, r2, m;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2))
return (arg1 * m + arg2 * m) / m
}
Number.prototype.add = function (arg) {
return accAdd(arg, this);
}
const accSub = (arg1, arg2) => {
var r1, r2, m, n;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2));
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
const accDiv = (arg1, arg2) => {
var t1 = 0, t2 = 0, r1, r2;
try { t1 = arg1.toString().split(".")[1].length } catch (e) { }
try { t2 = arg2.toString().split(".")[1].length } catch (e) { }
while (Math) {
r1 = Number(arg1.toString().replace(".", ""))
r2 = Number(arg2.toString().replace(".", ""))
return (r1 / r2) * Math.pow(10, t2 - t1);
}
}
Number.prototype.div = function (arg) {
return accDiv(this, arg);
}
const accMul = (arg1, arg2) => {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try { m += s1.split(".")[1].length } catch (e) { }
try { m += s2.split(".")[1].length } catch (e) { }
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
}
Number.prototype.mul = function (arg) {
return accMul(arg, this);
}
const getAttributeObj = (obj, path, fallback) => {
const parts = path.split(".");
const key = parts.shift();
if (typeof obj[key] !== "undefined") {
return parts.length > 0 ?
getAttributeObj(obj[key], parts.join("."), fallback) :
obj[key];
}
return fallback;
}
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const toFixedNum = (n, fixed) => `${n}`.match(new RegExp(`^-?\d+(?:.\d{0,${fixed}})?`))[0]
const roundNum = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)
const replenishZero = (num, len, zero = 0) => num.toString().padStart(len, zero)
const strParse = (str) => JSON.parse(str.replace(/(\w+)\s*:/g, (_, p1) => `"${p1}":`).replace(/\'/g, "\""))
const uuid = (a) => (a ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid))
uuid()
const sleep = async (t) => new Promise((resolve) => setTimeout(resolve, t));
class Observer {
caches = {};
on (eventName, fn){
this.caches[eventName] = this.caches[eventName] || [];
this.caches[eventName].push(fn);
}
emit (eventName, data) {
if (this.caches[eventName]) {
this.caches[eventName]
.forEach(fn => fn(data));
}
}
off (eventName, fn) {
if (this.caches[eventName]) {
const newCaches = fn
? this.caches[eventName].filter(e => e !== fn)
: [];
this.caches[eventName] = newCaches;
}
}
}
const dataType = (tgt, type) => {
const dataType = Object.prototype.toString.call(tgt).replace(/\[object (\w+)\]/, "$1").toLowerCase();
return type ? dataType === type : dataType;
}
const isNullArray = (arr) => {
return Array.isArray(arr) && !arr.length;
}
const isNullObject = (obj) => {
return dataType(obj, "object") && !Object.keys(obj).length;
}
const FilterXss = (content) => {
let elem = document.createElement("div");
elem.innerText = content;
const result = elem.innerHTML;
elem = null;
return result;
}
const ParseUrlSearch = () => {
return location.search.replace(/(\^?)|(&\$)/g, "").split("&").reduce((t, v) => {
const [key, val] = v.split("=");
t[key] = decodeURIComponent(val);
return t;
}, {});
}
const StringifyUrlSearch = (search = {}) => {
return Object.entries(search).reduce(
(t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`,
Object.keys(search).length ? "?" : ""
).replace(/&$/, "");
}
const hexToRGB = (hex) => {
var hexx = hex.replace('#', '0x')
var r = hexx >> 16
var g = hexx >> 8 & 0xff
var b = hexx & 0xff
return `rgb(${r}, ${g}, ${b})`
}
const RGBToHex = (rgb) => {
var rgbArr = rgb.split(/[^\d]+/)
var color = rgbArr[1]<<16 | rgbArr[2]<<8 | rgbArr[3]
return '#'+ color.toString(16)
}
export {
getType,
getUrlQuery,
addStrings,
reverseWords,
binarySearch,
intersection,
twoSumIndex,
calculate,
validIPAddress,
filterByID,
unicodeLength,
deepClone,
isMobile,
getGlobal,
mergeSort,
sortArray,
newArray,
add,
Scheduler,
PromiseQueue,
toDecimal,
toDecimal2,
fomatFloat,
accAdd,
accSub,
accMul,
accDiv,
getAttributeObj,
escapeHtml,
toFixedNum,
roundNum,
replenishZero,
strParse,
uuid,
sleep,
Observer,
isNullArray,
isNullObject,
dataType,
FilterXss,
ParseUrlSearch,
StringifyUrlSearch,
hexToRGB,
RGBToHex,
}
二、 h5优化样式
// 移动端H5兼容
body {
// 苹果系统上非<body>元素的滚动操作可能会存在卡顿
//-webkit-overflow-scrolling: touch;
//在手机端 iOS 13 系统中,-webkit-overflow-scrolling:touch 也会使 z-index 失效,将 touch 换成 unset
-webkit-overflow-scrolling: unset;
// 禁止屏幕抖动
padding-right: calc(100vw - 100%);
}
.elem {
overflow: auto;
}
// 禁止滚动传播
.overscroll-contain {
overscroll-behavior: contain;
}
// 禁止长按操作
* {
user-select: none;
-webkit-touch-callout: none;
// 禁止字体调整
text-size-adjust: 100%;
// 禁止高亮显示:触摸元素会出现半透明灰色遮罩,不想要!
-webkit-tap-highlight-color: transparent;
// 识别文本换行
white-space: pre-line;
// 屏幕旋转为横屏时,字体大小会变(禁止)
-webkit-text-size-adjust: 100%;
}
// 声明user-select:none会让<input>和<textarea>无法输入文本,可对其声明user-select:auto排除在外。
input,
textarea {
user-select: auto;
}
.transform-3d {
// 禁止动画闪屏
perspective: 1000;
backface-visibility: hidden;
transform-style: preserve-3d;
// 开启硬件加速
transform: translate3d(0, 0, 0);
}
// 美化滚动占位
::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
border-radius: 3px;
background-image: linear-gradient(135deg, #09f, #3c9);
}
// 美化输入占位:输入框占位文本太丑,::-webkit-input-placeholder来帮你。
input::-webkit-input-placeholder {
color: #66f;
}
// 对齐输入占位
input {
line-height: normal;
}
// input密码框password在ie/edge浏览器下默认的icon去除
// ie
input::-ms-reveal {
display: none;
}
input::-ms-clear {
display: none;
}
// edge
input::-o-clear {
display: none;
}
::-ms-clear,
::-ms-reveal {
display: none;
}
// 对齐下拉选项
// 下拉框选项默认向左对齐,是时候改改向右对齐了。
select option {
direction: rtl;
}
// 修复点击无效
// 在苹果系统上有些情况下非可点击元素监听click事件可能会无效,针对该情况只需对不触发click事件的元素声明cursor:pointer就能解决。
.cursor {
cursor: pointer;
}
// 画三角形
.triangle {
border: 50px solid transparent;
width: 0;
height: 0;
&.left {
border-right-color: #f66;
}
&.right {
border-left-color: #f66;
}
&.top {
border-bottom-color: #f66;
}
&.bottom {
border-top-color: #f66;
}
&.left-top {
border-left-color: #f66;
border-top-color: #f66;
}
&.left-bottom {
border-left-color: #f66;
border-bottom-color: #f66;
}
&.right-top {
border-right-color: #f66;
border-top-color: #f66;
}
&.right-bottom {
border-right-color: #f66;
border-bottom-color: #f66;
}
}
// 禁止长按
.long-press{
// 禁止长按图片保存
img {
-webkit-touch-callout: none;
pointer-events: none; // 像微信浏览器还是无法禁止,加上这行样式即可
}
// 禁止长按选择文字
div {
-webkit-user-select: none;
}
// 禁止长按呼出菜单
div {
-webkit-touch-callout: none;
}
}
三、富文本解析js
// 正则变量
var graceRichTextReg;
// 批量替换的样式 [ 根据项目需求自行设置 ]
var GRT = [ // div 样式 ['div', "line-height:2em;"],
// h1 样式
['h1', "font-size:3em; line-height:1.5em;"],
// h2 样式
['h2', "font-size:2em; line-height:1.8em;"],
// h3 样式
['h3', "font-size:1.6em; line-height:2em;"],
// h4 样式
['h4', "font-size:1.2em; line-height:2em;"],
// h5 样式
['h5', "font-size:1em; line-height:2em;"],
// h6 样式
['h6', "font-size:0.9em; line-height:2em;"],
// p 样式
['p', "margin:0;padding:0;overflow:hidden;"],
// b 样式
['b', "font-size:1em; line-height:2em;"],
// strong 样式
['strong', "font-size:1em; line-height:2em;"],
// code 样式
['code', "font-size:1em; line-height:1.2em; background:#F6F7F8; padding:8px 2%; width:96%;"],
// img 样式
['img', "width:100%; margin:0px 0;float:left;"],
// blockquote
['blockquote', "font-size:1em; border-left:3px solid #D1D1D1; line-height:2em; border-radius:5px; background:#F6F7F8; padding:8px 2%;"],
// li 样式
['ul', "padding:5px 0; list-style:none; padding:0; margin:0;"],
['li', "line-height:1.5em; padding:5px 0; list-style:none; padding:0; margin:0; margin-top:10px;"],
// table
['table', "width:100%; border-left:1px solid #F2F3F4; border-top:1px solid #F2F3F4;"],
['th', "border-right:1px solid #F2F3F4; border-bottom:1px solid #F2F3F4;"],
['td', "border-right:1px solid #F2F3F4; border-bottom:1px solid #F2F3F4; padding-left:5px;"]
];
module.exports = {
format : function(html){
// html = html.replace(/<pre.*pre>?/gis, function(word){
// word = word.replace(/[\n]/gi,'<br />');
// word = word.replace(/ /gi,'<span style="padding-left:2em;"></span>');
// return word.replace(/[\t]/gi, '<span style="padding-left:2em;"></span>');
// });
// console.log(html)
html = html.replace(/<pre/gi, '<p ');
html = html.replace(/<\/pre/gi,"</p");
for(let i = 0; i < GRT.length; i++){
graceRichTextReg = new RegExp('<'+GRT[i][0]+'>|<'+GRT[i][0]+' (.*?)>', 'gi');
html = html.replace(graceRichTextReg , function(word){
// 分析 dom 上是否带有 style=""
if(word.indexOf('style=') != -1){
var regIn = new RegExp('<' + GRT[i][0] + '(.*?)style="(.*?)"(.*?)(/?)>', 'gi');
return word.replace(regIn, '<'+ GRT[i][0] +'$1style="$2 ' + GRT[i][1] +'"$3$4>');
}else{
var regIn = new RegExp('<' + GRT[i][0] + '(.*?)(/?)>', 'gi');
return word.replace(regIn, '<'+ GRT[i][0] +'$1 style="' + GRT[i][1] +'$2">');
}
});
}
return html;
}
}