MENU

「合集」常用的 JavaScript 代码

2018 年 11 月 04 日 • 开发

document

焦点不在当前窗口时改变标题

document.addEventListener('visibilitychange', function () {
  if (document.hidden) {
    document.title = '焦点不在当前窗口';
  } else {
    document.title = '焦点在当前窗口';
  }
});

解除离开页面检测

document.hasFocus = function () {
  return true;
};

使网页内容(body)可编辑

开启

document.body.contentEditable = 'true';
document.designMode = 'on';
(function () {
  let items = document.querySelectorAll('iframe');
  for (let i = 0; i < items.length; i++) {
    try {
      items[i].contentDocument.body.contentEditable = 'true';
      items[i].contentDocument.designMode = 'on';
    } catch (err) {
      console.log(err);
    }
  }
})();

关闭

document.body.contentEditable = 'false';
document.designMode = 'off';
(function () {
  let items = document.querySelectorAll('iframe');
  for (let i = 0; i < items.length; i++) {
    try {
      items[i].contentDocument.body.contentEditable = 'false';
      items[i].contentDocument.designMode = 'off';
    } catch (err) {
      console.log(err);
    }
  }
})();

function

查找元素的 Vue 对象

/**
 * @typedef  {object} FunctionResult
 * @property {boolean}     state   是否查找成功
 * @property {HTMLElement} element Vue 对象所在的元素
 * @property {object}      data    找到的 Vue 对象
 * @property {object[]}    parents 父 Vue 对象
 */

/**
 * @description 查找元素的 Vue 对象
 * @param   {HTMLElement} el 需要查找的元素
 * @returns {FunctionResult} 返回查找结果信息
 */
function findElementVue(el) {

  /** @type {FunctionResult} */
  const result = {
    state: false,
    element: null,
    data: null,
    parents: [],
  };

  const attrName = '__vue__';

  // 查找属性
  while (el) {
    const data = el[attrName];
    if (data) {
      result.state = true;
      result.element = el;
      result.data = data;
      break;
    } else {
      el = el.parentElement;
    }
  }

  // 查找父对象
  if (result.state) {
    let attrName = '$parent';
    let parent = result.data[attrName];

    while (parent) {
      result.parents.push(parent);
      parent = parent[attrName];
    }
  }

  return result;

}

查找字符 charstr 中第 num 次出现的位置

function findChar(str = '', char = '', num = 1) {
  var index = str.indexOf(char);
  num = num - 1;
  if (num > 0) {
    for (var i = 0; i < num; i++) {
      index = str.indexOf(char, index + 1);
    }
  }
  return index;
}

格式化时间

/**
  * @description 格式化时间
  * @param   {(number|string|Date)} [time] 要格式化的时间(默认当前)
  * @param   {string} [format] 需要的格式(默认 yyyy-mm-dd hh:ii:ss)
  * @returns {string} 格式化后的时间
  */
function formatTime(time = new Date(), format = 'yyyy-mm-dd hh:ii:ss') {

  let timeType = typeof time;

  /** @type {Date} */
  let dateObj = null;

  if (timeType == 'number' || timeType == 'string') {
    dateObj = new Date(time);
  } else if (timeType == 'object') {
    dateObj = time;
  }

  // 时间信息
  let timeInfo = {
    // 年月日
    y: dateObj.getFullYear(),
    m: dateObj.getMonth() + 1,
    d: dateObj.getDate(),
    // 时分秒
    h: dateObj.getHours(),
    i: dateObj.getMinutes(),
    s: dateObj.getSeconds(),
  };

  // 格式占位符正则
  let formatReg = {
    y: /y+/g,
    m: /m+/g,
    d: /d+/g,
    h: /h+/g,
    i: /i+/g,
    s: /s+/g,
  };

  for (let key in formatReg) {

    // 正则匹配
    let matched = format.match(formatReg[key]);

    // 获取对应的时间
    let timeValue = String(timeInfo[key]);

    // 无匹配结果
    if (!matched) {
      continue;
    }

    // 根据匹配结果(位数)进行替换
    matched.forEach(function (v) {
      let tLength = timeValue.length;
      let vLength = v.length;
      // 长度不足,补零
      if (tLength < vLength) {
        timeValue = timeValue.padStart(v.length, '0');
      }
      // 长度超出,截取
      // if (tLength > vLength) {
      //   timeValue = timeValue.substring(tLength - vLength);
      // }
      // 替换对应的值
      format = format.replace(v, timeValue);
    });

  }

  return format;

}

获取随机的十六进制色

/** 获取随机的十六进制色 */
function getHEXColor() {
  var codes = [
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
  ];
  var colors = ['#'];
  for (let i = 0; i < 6; i++) {
    let index = Math.floor(Math.random() * 16);
    colors.push(codes[index]);
  }
  return colors.join('');
}

获取元素的边界信息

/**
 * @description 获取元素的边界信息(当前元素必须在父元素内)
 * @param {Element} curr   当前 DOM 元素
 * @param {Element} parent 父 DOM 元素
 */
function getBoundaryInfo(curr, parent) {

  if (!curr || !parent) {
    console.error('获取失败,缺少参数!');
    return null;
  }

  var result = {};
  var currRect = curr.getBoundingClientRect();
  var parentRect = parent.getBoundingClientRect();

  // 当前元素四角坐标和宽高
  result.curr = {
    x0: currRect.left,
    x1: currRect.right,
    y0: currRect.top,
    y1: currRect.bottom,
    w: currRect.width,
    h: currRect.height,
  };

  // 父元素四角坐标
  result.parent = {
    x0: parentRect.left,
    x1: parentRect.right,
    y0: parentRect.top,
    y1: parentRect.bottom,
    w: parentRect.width,
    h: parentRect.height,
  };

  // 距离
  result.distance = {
    top: result.curr.y0 - result.parent.y0,
    bottom: result.parent.y1 - result.curr.y1,
    left: result.curr.x0 - result.parent.x0,
    right: result.parent.x1 - result.curr.x1,
  };

  return result;

}

获取元素的坐标信息

/**
 * @description 获取元素的坐标信息(四个角以及宽高)
 * @param {Element} element DOM 元素
 */
function getElemPosInfo(element) {
  if (!element) {
    console.error('获取失败,缺少参数!');
    return null;
  }
  let rect = element.getBoundingClientRect();
  let data = {
    x0: rect.left,
    x1: rect.right,
    y0: rect.top,
    y1: rect.bottom,
    w: rect.width,
    h: rect.height,
  };
  return data;
}

获取月份第一天和最后一天的时间戳

/**
 * @description 获取月份第一天和最后一天的时间戳
 * @param {number} year 年份
 * @param {number} month 月份
 * @returns `{ start: 第一天, end: 最后一天 }`
 */
function getTimestampOfMonth(year, month) {
  var start = new Date(year, month - 1, 1)
  var end = new Date(year, month, 0);
  var time = {
    start: start.getTime(),
    end: end.getTime()
  };
  return time;
}

获取坐标下方的元素

/**
 * @description 获取坐标下方的元素(从子元素到父元素)
 * @param {number} x 横坐标
 * @param {number} y 纵坐标
 */
function elemsFromPoint(x, y) {

  if (x === undefined || y === undefined) {
    return [];
  }

  x = Math.floor(x);
  y = Math.floor(y);

  let item = document.elementFromPoint(x, y);
  let items = [];

  if (item) {
    items.push(item);
  } else {
    return [];
  }

  while (item.parentElement) {
    item = item.parentElement;
    items.push(item);
  }

  return items;

};

计算字符串的字符数

/**
 * @description 计算字符串的字符数(数字英文字母 +1,其他 +3)
 * @param   {string} str 被检测的字符串
 * @returns {number} 字符数
 */
function calcChars(str) {
  var reg = /[0-9a-zA-Z]/;
  var sum = 0;
  for (let i in str) {
    if (reg.test(str.charAt(i))) {
      sum += 1;
    } else {
      sum += 3;
    }
  }
  return sum;
}

加减法精度

/**
 * @description 加减法精度
 * @param   {string} type 类型(plus、sub)
 * @param   {number} [num1] 数值1,默认为 0
 * @param   {number} [num2] 数值2,默认为 0
 * @returns {(number|null)} 返回两个数值相加或相减后的结果
 */
function accPlusAndSub(type, num1 = 0, num2 = 0) {

  var decimalsNum1 = (String(num1).split('.')[1] || '').length;
  var decimalsNum2 = (String(num2).split('.')[1] || '').length;
  var decimalsMax = Math.max(decimalsNum1, decimalsNum2);
  var multiplies = Math.pow(10, decimalsMax);

  if (type === 'plus') {
    return ((num1 * multiplies + num2 * multiplies) / multiplies);
  } else if (type === 'sub') {
    return ((num1 * multiplies - num2 * multiplies) / multiplies);
  } else {
    return null;
  }

}

检测用户是否离开页面

/**
 * @description 检测用户是否离开页面(一秒检测一次)
 * @param {object}   [options] 配置选项
 * @param {function} [options.onblur]    用户离开页面时的回调函数
 * @param {function} [options.onfocus]   用户返回页面时的回调函数
 * @param {string}   [options.blurDelay] 设定用户离开页面多久后才调用 onblur
 * - 单位为秒
 * - 默认 0
 * @param {string}   [options.timerName] 定时器名称
 * - 用于 setInterval()
 * - 默认 tCheckPageBlur
 */
function checkPageBlur(options) {

  var config = {
    onblur: null,
    onfocus: null,
    blurDelay: 0,
    timerName: 'tCheckPageBlur',
  };

  Object.assign(config, options);

  var timerName = config.timerName; // 定时器名称
  var checkDelay = 0;               // 延时
  var blurTriggered = false;        // 标记状态

  clearInterval(window[timerName]);
  window[timerName] = setInterval(function () {
    var isFocus = document.hasFocus();

    if (isFocus && blurTriggered) {
      // 在页面且触发过 blur
      blurTriggered = false;
      checkDelay = 0;
      try {
        config.onfocus && (config.onfocus());
      } catch (err) {
        console.error('[检测] 回调函数 onfocus 出错\n', err);
      }
    } else if (!isFocus && !blurTriggered) {
      // 不在页面且未触发 blur
      if (checkDelay >= config.blurDelay) {
        blurTriggered = true;
        checkDelay = 0;
        try {
          config.onblur && (config.onblur());
        } catch (err) {
          console.error('[检测] 回调函数 onblur 出错\n', err);
        }
      } else {
        checkDelay += 1;
      }
    }
  }, 1000);

}

// 调用
checkPageBlur({
  onblur: function () {
    console.log('[检测] 用户离开页面');
  },
  onfocus: function () {
    console.log('[检测] 用户返回页面');
  },
});

解析 URL 地址的参数(?=...)为一个对象

function queriesToObj(url = '') {
  var split = url.split('?')[1];
  var arr = split.split('&');
  var obj = {};

  arr.forEach(function (item) {
    var kv = item.split('=');
    obj[kv[0]] = kv[1];
  });

  return obj;
}

矩形碰撞检测

/**
 * @description 矩形碰撞检测
 * @param   {Element} elemA        当前元素
 * @param   {Element} elemB        目标元素
 * @param   {boolean} [checkAside] 是否包含边缘碰撞,默认包含
 * @returns {object} `{ error: 是否检测失败, hit: 是否碰撞 }`
 */
function rectColisionCheck(elemA, elemB, checkAside = true) {
  const result = {
    error: false,
    hit: false,
  };

  if (!(elemA && elemB)) {
    console.error('缺少参数');
    result.error = true;
    return result;
  }

  const rectA = elemA.getBoundingClientRect();
  const rectB = elemB.getBoundingClientRect();

  if (checkAside) {
    result.hit = !(
      rectA.bottom < rectB.top ||
      rectA.left > rectB.right ||
      rectA.top > rectB.bottom ||
      rectA.right < rectB.left
    );
  } else {
    result.hit = !(
      rectA.bottom <= rectB.top ||
      rectA.left >= rectB.right ||
      rectA.top >= rectB.bottom ||
      rectA.right <= rectB.left
    );
  }

  return result;
}

设置事件对象属性

/**
 * @description 设置事件对象属性
 * @param {Eveny}  ev 事件对象
 * @param {object} props 要设置的属性
 */
function setEvProps(ev, props = {}) {
  if (ev) {
    for (let key in props) {
      Object.defineProperty(ev, key, {
        configurable: true,
        enumerable: true,
        get: function () {
          return props[key];
        },
      });
    }
  }
}

通过点路径访问对象属性

/**
 * @description 通过点路径访问对象属性
 * @param {object} obj
 * @param {string} path
 */
function getObjValue(obj, path = '') {

  if (typeof obj !== 'object') {
    console.error('访问失败,参数 obj 错误!');
    return;
  }

  if (typeof path !== 'string') {
    console.error('访问失败,参数 path 错误!');
    return;
  }

  if (path === '') {
    return obj;
  }

  return path.split('.').reduce((a, b) => {
    return (a === undefined ? a : a[b]);
  }, obj);

}

暂停执行代码一段时间

/**
 * @description 暂停执行代码一段时间
 * @param   {number} [time] 时长(毫秒),默认 1000
 * @returns {Promise} Promise
 */
function sleep(time = 1000) {
  return new Promise(function (resolve) {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

await sleep(1000);

字符串转文件

/**
 * @description 字符串转文件
 * @param {string} data 字符串数据
 * @param {string} filename 文件名
 * @param {string} filetype 文件类型(MIME)
 */
function strToFile(data = '', filename = 'export.txt', filetype = 'text/plain') {

  // 转为 Blob
  var strToBlob = new Blob([data], { type: filetype });
  // URL 对象兼容性处理
  var urlObject = window.URL || window.webkitURL || window;
  // 创建对象 URL
  var blobURL = urlObject.createObjectURL(strToBlob);
  // 创建 a 元素
  var aElem = document.createElement('a');

  // 设置属性
  aElem.classList.add('hidden');
  aElem.download = filename;
  aElem.href = blobURL;
  aElem.target = '_blank';

  // 添加元素
  document.body.appendChild(aElem);

  // 模拟点击
  aElem.click();

  // 移除元素
  aElem.remove();

  // 释放对象
  urlObject.revokeObjectURL(blobURL);

}
最后编辑于: 2022 年 07 月 23 日