# xss 解决方案 - 过滤输入

export function encodeHtml(str) {
  const codeMaps = {
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&apos;',
    '&': '&amp;'
  };
  // /[<>"'&]/g
  const reg = new RegExp('[' + Object.keys(codeMaps).join('') + ']', 'g');
  return str.replace(reg, (v, i, str) => codeMaps[v]);
}
1
2
3
4
5
6
7
8
9
10
11
12

# 数字补 0

/**
 * 数字补0
 * @param {*} number 数字
 * @returns
 */
export function doubleNumber(number) {
  if (typeof number !== 'number') return number;
  return 0 < number && number < 9 ? '0' + number : '' + number;
}
1
2
3
4
5
6
7
8
9

# 获取区间段的随机整值

/**
 * 获取区间段的随机整值
 * @params min 最小值
 * @params max 最大值
 **/
export function randomNum(min, max) {
  return Math.round(Math.random() * (max - min) + min);
}
1
2
3
4
5
6
7
8

# 获取随机颜色一

/**
 * 获取随机颜色一
 * rgb颜色 rgb(255 ,255 ,255 )
 **/
export function randomColor() {
  return `rgb(${randomNum(0, 255)},${randomNum(0, 255)},${randomNum(0, 255)})`;
  // return `rgb(${Math.round(Math.random()*255)},${Math.round(Math.random()*255)},${Math.round(Math.random()*255)})`
}
1
2
3
4
5
6
7
8

# 获取随机颜色二

/**
 * 获取随机颜色二
 * 十六进制颜色 #ff22ff
 **/
export function randomNum() {
  return Math.round(Math.random() * 0xffffff).toString(16);
}
1
2
3
4
5
6
7

# 深拷贝一

/**
 * 深拷贝 另外可借鉴jquery的$.extend(boolean,arg1,arg2,...)方法
 * 对象 / 数组 /
 **/
export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'shallowClone');
  }
  const targetObj = source.constructor === Array ? [] : {};
  for (const keys in source) {
    if (source.hasOwnProperty(keys)) {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = deepClone(source[keys]);
      } else {
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 深拷贝二

/**
 * 深拷贝正则
 **/
export function cloneReg(target, isDeep) {
  const regFlag = /\w*$/;
  const result = new target.constructor(target.source, regFlag.exec(target));
  //const result = new target.constructor(target.source , target.flags) ES6属性
  if (isDeep) {
    result.lastIndex = 0;
  } else {
    result.lastIndex = target.lastIndex;
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 金额千分位显示


/**
 * 金额千分位显示
 * @param {*} value
 */
export function formatAmount (value) {
  if (
    value === null ||
    value === undefined ||
    value === '' ||
    (value + '').trim() === ''
  ) {
    return '';
  }
  try {
    value = Number(value);
    return value.toLocaleString();
  } catch (err) {
    return value;
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 利用正则实现金额千分位格式化

export function formatAmount(value) {
  return ('' + value).replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
}
1
2
3

# 检测平台(设备)类型

const uA = navigator.userAgent;
let isWechat = /micromessenger/i.test(uA),
  isWeibo = /weibo/i.test(uA),
  isQQ = /qq\//i.test(uA),
  isIOS = /(iphone|ipod|ipad|ios)/i.test(uA),
  isAndroid = /android/i.test(uA);
1
2
3
4
5
6

# 常用正则

// 匹配邮箱
let reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$

// (新)匹配手机号
let reg = /^1[0-9]{10}$/;
// (旧)匹配手机号
let reg = /^1(3|4|5|7|8)[0-9]{9}$/;

// 匹配8-16位数字和字母密码的正则表达式
let reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/;

// 匹配国内电话号码 0510-4305211
let reg = /\d{3}-\d{8}|\d{4}-\d{7}/;

// 匹配身份证号码
let reg=/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;

// 匹配腾讯QQ号
let reg = /[1-9][0-9]{4,}/;

// 匹配ip地址
let reg = /\d+\.\d+\.\d+\.\d+/;

// 匹配中文
let reg = /^[\u4e00-\u9fa5]*$/;
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

# 跨端事件处理

// 是否支持触摸事件
let isSupportTouch = 'ontouchstart' in document.documentElement ? true : false;

//禁用Enter键表单自动提交
document.onkeydown = function(event) {
  let target, code, tag;
  if (!event) {
    event = window.event; //针对ie浏览器
    target = event.srcElement;
    code = event.keyCode;
    if (code == 13) {
      tag = target.tagName;
      if (tag == 'TEXTAREA') {
        return true;
      } else {
        return false;
      }
    }
  } else {
    target = event.target; //针对遵循w3c标准的浏览器,如Firefox
    code = event.keyCode;
    if (code == 13) {
      tag = target.tagName;
      if (tag == 'INPUT') {
        return false;
      } else {
        return 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

# 移动端适配方案

(function(doc, win) {
  var docEl = doc.documentElement,
    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    recalc = function() {
      var clientWidth = docEl.clientWidth;
      var fontSize = 20;
      docEl.style.fontSize = fontSize + 'px';
      var docStyles = getComputedStyle(docEl);
      var realFontSize = parseFloat(docStyles.fontSize);
      var scale = realFontSize / fontSize;
      console.log('realFontSize: ' + realFontSize + ', scale: ' + scale);
      fontSize = (clientWidth / 667) * 20;
      if (isIphoneX()) fontSize = 19;
      fontSize = fontSize / scale;
      docEl.style.fontSize = fontSize + 'px';
    };
  // Abort if browser does not support addEventListener
  if (!doc.addEventListener) return;
  win.addEventListener(resizeEvt, recalc, false);
  doc.addEventListener('DOMContentLoaded', recalc, false);

  // iphoneX判断
  function isIphoneX() {
    return (
      /iphone/gi.test(navigator.userAgent) &&
      screen.height == 812 &&
      screen.width == 375
    );
  }
})(document, window);
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

# 生成星级评分

const StartScore = rate => '★★★★★☆☆☆☆☆'.slice(5 - rate, 10 - rate);
const start = StartScore(3);
// start => "★★★☆☆"
1
2
3

# 全屏居中

//自动居中函数
function autoCenter(el) {
  var bodyX = document.documentElement.offsetWidth || document.body.offsetWidth;
  var bodyY =
    document.documentElement.offsetHeight || document.body.offsetHeight;

  var elementX = el.offsetWidth;
  var elementY = el.offsetHeight;

  el.style.left = (bodyX - elementX) / 2 + 'px';
  el.style.top = (bodyY - elementY) / 2 + 'px';
}
1
2
3
4
5
6
7
8
9
10
11
12

# 监听 iframe 下载完成

定时器轮询监听 readyState 的状态,如果是 complete 或者 interactive 说明文件加载完成。

let iframe = document.createElement('iframe');
iframe.src = path;
iframe.style.display = 'none';
document.body.appendChild(iframe);
const timer = setInterval(() => {
  const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  if (
    iframeDoc.readyState == 'complete' ||
    iframeDoc.readyState == 'interactive'
  ) {
    document.body.removeAttribute(iframe);
    clearInterval(timer);
    resolve('success');
  }
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 判断当前位置是否为页面底部

function bottomVisible() {
  return (
    document.documentElement.clientHeight + window.scrollY >=
    (document.documentElement.scrollHeight ||
      document.documentElement.clientHeight)
  );
}
1
2
3
4
5
6
7

# 判断元素是否在可视范围内

// partiallyVisible 为是否为完全可见
function elementIsVisibleInViewport(el, partiallyVisible = false) {
  const { top, left, bottom, right } = el.getBoundingClientRect();
  return partiallyVisible
    ? ((top > 0 && top < innerHeight) ||
        (bottom > 0 && bottom < innerHeight)) &&
        ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
    : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
}
1
2
3
4
5
6
7
8
9

# 固定滚动条

/**
 * 功能描述:一些业务场景,如弹框出现时,需要禁止页面滚动,
 * 这是兼容安卓和 iOS 禁止页面滚动的解决方案
 */

let scrollTop = 0;

function preventScroll() {
  // 存储当前滚动位置
  scrollTop = window.scrollY;

  // 将可滚动区域固定定位,可滚动区域高度为 0 后就不能滚动了
  document.body.style['overflow-y'] = 'hidden';
  document.body.style.position = 'fixed';
  document.body.style.width = '100%';
  document.body.style.top = -scrollTop + 'px';
  // document.body.style['overscroll-behavior'] = 'none'
}

function recoverScroll() {
  document.body.style['overflow-y'] = 'auto';
  document.body.style.position = 'static';
  // document.querySelector('body').style['overscroll-behavior'] = 'none'

  window.scrollTo(0, scrollTop);
}
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

# 把有连字符号的字符串转化为驼峰命名法的字符串

function toCamelCase(value) {
  return value.replace(/-(\w)/g, (matched, letter) => letter.toUpperCase());
}
1
2
3

# 滚动条平滑滚动回到顶部

function scrollToTop() {
  let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  if (scrollTop > 0) {
    window.requestAnimationFrame(scrollToTop);
    window.scrollTo(0, scrollTop - scrollTop / 8);
  } else {
    window.cancelAnimationFrame(scrollToTop);
  }
}
1
2
3
4
5
6
7
8
9

# 函数柯里化

function add() {
  let args = [...arguments];

  function _add() {
    args.push(...arguments);
    return _add;
  }

  _add.toString = function() {
    return args.reduce((pre, cur) => {
      return pre + cur;
    });
  };
  return _add;
}
console.log(add(1, 2)(3, 4)(5)(6)()().toString());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 禁止右键、选择、复制

['contextmenu', 'selectstart', 'copy'].forEach(function(ev) {
  document.addEventListener(ev, function(e) {
    if (e && e.preventDefault) e.preventDefault();
    else window.event.returnValue = false;
    return false;
  });
});
1
2
3
4
5
6
7

# url 添加参数

/**
 * url添加参数
 * @param {String} url
 * @param {Object} params
 */
export function addUrlParams(url, params) {
  Object.keys(params).forEach(key => {
    const parameter = `${key}=${params[key]}`;
    if (url.indexOf(`${key}=`) > 0) {
      url = url.replace(new RegExp(`${key}=([^&]*)`, 'g'), parameter);
    } else {
      if (!url.includes('?')) {
        url += '?';
      } else if (url.indexOf('?') !== url.length - 1) {
        url += '&';
      }
      url += parameter;
    }
  });
  return url;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Usage

let str1 = this.addUrlParams(url, {
  u_code: this.getLoginAfterData.id,
  a: 'dx'
});
let str2 = this.addUrlParams(str1, {
  authRight: localStorage.getItem('meetingManager_authRight')
});
1
2
3
4
5
6
7

# url 获取参数

//从URL中搜索某个参数
function getQueryString(name) {
  var reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`);
  var r = window.location.search.substr(1).match(reg);
  if (r != null) return decodeURIComponent(r[2]);
  return null;
}
1
2
3
4
5
6
7

Usage

URL: 'https://xxx.xx/111.html?name=xxx&age=20';
console.log(getQueryString(age)); // 20
1
2

增加/替换 URL 参数

function changeURLArg(url, arg, value) {
  var pattern = `${arg}=([^&]*)`;
  var replaceText = `${arg}=${value}`;
  if (url.match(pattern)) {
    var tmp = `/(${arg}=)([^&]*)/g`; //是否区分大小写
    tmp = url.replace(eval(tmp), replaceText);
    return tmp;
  } else {
    if (url.match('[?]')) {
      return url + '&' + replaceText;
    } else {
      return url + '?' + replaceText;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Usage

window.location.href = changeURLArg(window.location.href, 'name', 'xxx');
1

# 异步函数组合

type PromiseFunctionType = (val?: any) => Promise<any>;
/**
 * 将1个promise函数或多个promise函数组成的数组包装成一个函数,按顺序执行
 * @param {Function | Array} promiseArrOrFunc
 * @return { Function }
 */
function flattenPromisesArray(
  promiseArrOrFunc: PromiseFunctionType | PromiseFunctionType[]
) {
  const fns: PromiseFunctionType[] = Array.isArray(promiseArrOrFunc)
    ? promiseArrOrFunc
    : [promiseArrOrFunc];
  return function(props: any) {
    return Promise.all(
      fns.reduce((p, fn) => p.then(() => fn(props)), Promise.resolve())
    );
  };
}

// 用例
// 可能是数组,也可能是函数
const arrOrFunction = [
  async (x: any) => {
    console.log('1', x);
    await sleep();
    console.log('1-1');
  },
  async (y: any) => {
    console.log('2', y);
    await sleep();
    console.log('2-1');
  }
];
function sleep(time = 1000) {
  return new Promise(resolve => {
    setTimeout(resolve, time);
  });
}
flattenPromisesArray(arrOrFunction)('xx');
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
/**
 * 异步串行调用
 * 思考:上一个promise的结果作为为参数传到下一个promise?
 */
function promiseSeries(promises) {
  return promises.reduce(
    (memo, cur) => memo.then(() => cur()),
    Promise.resolve()
  );
}
1
2
3
4
5
6
7
8
9
10