首页 > 技术文章 > DOM封装

yangguanglei 2020-11-02 19:33 原文

DOM操作封装

/**
 * 动态计算div高度
 * @param {String} id1,父级id
 * @param {String} id2,兄弟id
 * @param {String} id3,要计算div的id
 */
export const calcHeight = function(id1, id2, id3) {
  setInterval(doSomething(), 1000); //自动刷新(每秒一次执行)
  function doSomething() {
    let div_height = document.getElementById(id2).offsetHeight; //div的实时高度
    let table_card = document.getElementById(id3);
    console.log(document.getElementById(id1).offsetHeight - div_height);
    table_card.style.height = document.getElementById(id1).offsetHeight - div_height - 36 + 'px';
  }
};

/**
 * 创建节点
 * @param {String} String 例如:<div>hi</div>
 */
export const createElement = function(String) {
  const container = document.createElement('template');
  container.innerHTML = String.trim();
  return container.content.children[0];
};

/**
 * 新增弟弟,找到父节点,插入到node后面兄弟节点的前面
 * @param {*} node
 * @param {*} node2
 */
export const afterElement = function(node, node2) {
  node.parentNode.insertBefore(node2, node.nextSibling);
};

/**
 * 新增哥哥,找到父节点,插入到node的前面
 * @param {*} node
 * @param {*} node2
 */
export const beforeElement = function(node, node2) {
  node.parentNode.insertBefore(node2, node);
};

/**
 * 新增儿子
 * @param {*} parent
 * @param {*} node
 */
export const appendElement = function(parent, node) {
  parent.appendChild(node);
};

/**
 * 新增老爹
 * @param {*} node
 * @param {*} parent
 */
export const wrapElement = function(node, parent) {
  beforeElement(node, parent);
  appendElement(parent, node);
};

/**
 * 删除节点,找到node的老爹,删除node,最后返回node的引用
 * @param {*} node
 */
export const removeElement = function(node) {
  node.parentNode.removeChild(node);
  return node;
};

/**
 * 删除后代,empty(parent),遍历删除子节点,返回引用
 * @param {*} node
 */
export const emptyElement = function(node) {
  const array = [];
  let _node = node.firstChild;
  while (_node) {
    array.push();
  }
};

/**
 * 读写属性,setAttribute(name),设置name属性;getAttribute(name,value),给属性name赋值value。
 * 当只有两个参数时,就读属性,返回属,当有三个参数时,就修改属性
 * @param {*} node 节点node
 * @param {*} name 属性名称
 * @param {*} value 属性值
 */
export const attr = function(node, name, value) {
  if (arguments.length === 2) {
    return node.getAttribute(name);
  } else if (arguments.length === 3) {
    node.setAttribute(name, value);
  }
};

/**
 * 读写文本内容。
 * 如果只有一个参数就读文本内容,返回node.innerText。
 * 如果有两个参数,就写文本内容 node.innerText = string。
 * @param {*} node 节点
 * @param {String} string 文本
 */
export const text = function(node, string) {
  if (arguments.length === 2) {
    if ('innerText' in node) {
      node.innerText = string;
    } else {
      node.textContent = string;
    }
  } else if (arguments.length === 1) {
    if ('innerText' in node) {
      return node.innerText;
    } else {
      return node.textContent;
    }
  }
};

/**
 * 读写HTML内容,两个参数替换,一个参数获取
 * @param {*} node html节点
 * @param {String} string 例如:"\<div>hello\</div>"
 */
export const html = function(node, string) {
  if (arguments.length === 2) {
    node.innerHTML = string;
  } else if (arguments.length === 1) {
    return node.innerHTML;
  }
};

/**
 * 修改node样式
 * @param {*} node 节点
 * @param {*} name 样式名
 * @param {String} value 样式值
 * @example style(div, 'color', 'red'),设置一个属性
 * @example style(div, 'color'),读一个属性
 * @example style(node, {'color': 'red', 'border': '1px solid black'}),传一个对象,同时设置多个属性
 */
export const style = function(node, name, value) {
  if (arguments.length === 3) {
    //dom.style(div, 'color', 'red')
    node.style[name] = value;
  } else if (arguments.length === 2) {
    if (typeof name === 'string') {
      //dom.style(div, 'color')
      return node.style[name];
    } else if (name instanceof Object) {
      //dom.style(div, {color: 'red'})
      const object = name;
      for (let key in object) {
        node.style[key] = object[key];
      }
    }
  }
};

/**
 * 添加样式
 * @param {*} node
 * @param {*} className
 */
export const addStyle = function(node, className) {
  node.classList.add(className);
};
/**
 * 删除样式
 * @param {*} node
 * @param {*} className
 */
export const removeStyle = function(node, className) {
  node.classList.remove(className);
};
/**
 * 查找样式
 * @param {*} node
 * @param {*} className
 */
export const hasStyle = function(node, className) {
  return node.classList.contains(className);
};

/**
 * 添加监听事件
 * @param {*} node
 * @param {*} eventName
 * @param {*} fn
 */
export const on = function(node, eventName, fn) {
  node.addEventListener(eventName, fn);
};

/**
 * 删除监听事件
 * @param {*} node
 * @param {*} eventName
 * @param {*} fn
 */
export const off = function(node, eventName, fn) {
  node.removeEventListener(eventName, fn);
};

/**
 * 查找标签,在指定区域或者全局document里找HTML标签。
 * scope可以不传,那就是在全局查找
 * @param {*} selector
 * @param {*} scope
 * @example find('#travel')[0]
 * @notice 返回的是伪数组,使用的时候把下标加上
 */
export const find = function(selector, scope) {
  return (scope | document).querySelectorAll(selector);
};

/**
 * 获取父元素
 * @param {*} node
 *
 */
export const parent = function(node) {
  return node.parentNode;
};

/**
 * 获取子元素
 * @param {*} node
 * @notice 返回的是伪数组,使用的时候把下标加上
 */
export const children = function(node) {
  return node.children;
};

/**
 * 获取兄弟姐妹元素
 * @param {*} node
 * @description 获取父元素的所有子元素,再将自己排除
 * @notice filter是array中的一个方法,作用是返回满足条件的所有元素
 */
export const sibling = function(node) {
  return Array.from(node.parentNode.children).filter((n) => n !== node);
};

/**
 * 获取弟弟
 * @param {*} node
 */
export const next = function(node) {
  let x = node.nextSibling;
  while (x && x.nodeType === 3) {
    x = x.nextSibling;
  }
  return x;
};

/**
 * 获取哥哥
 * @param {*} node
 */
export const previous = function(node) {
  let x = node.previousSibling;
  while (x && x.nodeType === 3) {
    x = x.previousSibling;
  }
  return x;
};

/**
 * 遍历所有节点
 * @param {*} nodeList
 * @param {*} fn 是一个传入的函数
 */
export const each = function(nodeList, fn) {
  for (let i = 0; i < nodeList.length; i++) {
    fn.call(null, nodeList[i]);
  }
};

/**
 * 获取元素位置(排在第几位)
 * @param {*} node
 */
export const index = function(node) {
  const list = children(node.parentNode);
  let i;
  for (i = 0; i < list.length; i++) {
    if (list[i] === node) {
      break;
    }
  }
  return i;
};

/**
 * 为元素el添加名为className的类名
 * @param {String} el
 * @param {String} className
 */
export const addClass = function(el, className) {
  // 添加之前先检查是否已经包含该类
  if (hasClass(el, className)) {
    return false;
  }
  let classArr = el.className.split(' ');
  classArr.push(className);
  el.className = classArr.join(' ');
};

/**
 * 检查元素el是否已经包含名为className的类名
 * @param {String} el
 * @param {String} className
 */
export const hasClass = function(el, className) {
  // 使用一个正则判断
  // (^|\s):直接开头或前面是空格;(\s|$):后面是空格或者直接结尾
  // 多出的一个反斜杠用于转义
  let reg = new RegExp(`(^|\\s)${className}(\\s|$)`);
  return reg.test(el.className);
};

/**
 * 获取指定dom上的属性值(第三个参数val,存在表示设置,不存在表示获取)
 * @param {*} el
 * @param {*} name
 * @param {*} val
 */
export function getData(el, name, val) {
  const prefix = 'data-';
  name = prefix + name;
  if (val) {
    return el.setAttribute(name, val);
  } else {
    return el.getAttribute(name);
  }
}

let elementStyle = document.createElement('div').style;

// 定义vendor:供应商
let vendor = (() => {
  let transformName = {
    webkit: 'webkitTransform',
    Moz: 'MozTransform',
    O: 'OTransform',
    ms: 'msTransform',
    standard: 'transform',
  };
  // 循环遍历transformName,如果element的style支持其中一个,就返回(内核标志)
  for (let key in transformName) {
    if (elementStyle[transformName[key]] !== undefined) {
      return key;
    }
  }
  // 如果都不满足,说明该浏览器有毛病,return false
  return false;
})();

/**
 * 为样式style拼接一个能够支持它的浏览器内核前缀
 * @param {*} style
 */
export function prefixStyle(style) {
  if (vendor === false) {
    return false;
  }
  if (vendor === 'standard') {
    return style;
  }
  // 然后就做拼接,内核缩写 + 属性名首字母大写 + 剩余部分
  // eg. 'webkit' + 'T' + 'ransform' = 'webkitTransform'
  return vendor + style.charAt(0).toUpperCase() + style.substr(1);
}

推荐阅读