dom相关工具类的封装函数

460 阅读2分钟

操作处理dom相关的方法在react和vue框架生态发展起来后很少用到的,用数据驱动代替dom操作,但并不表示dom不重要。在vue自定义指令,vue3的composition api以及自定义的react hooks中或许还得使用到。下面记录封装一些dom相关的工具类函数。

获取元素的子节点

function elemChildren(node) {
    // 返回类数组
    var temp = {
        length: 0,
        push: Array.prototype.push,
        splice: Array.prototype.splice
     }
     
    var children = node.childNodes,
        len = children.length
        item
    
    for (var i = 0; i < len; i++) {
        item = childen[i]
        // 只需返回元素节点,忽略其它节点
        if (item.nodeType === 1) {
            item.push(item)
        }
        
    return temp
  }

寻找元素第n层父节点

function elemParent(node, n) {
    var type = typeof n
    
    if (type === 'undefined') {
        return node.parentNode
    } else if (n <= 0 || type !== 'number') {
        return undefined
    }
    
    while(n) {
        node = node.parentNode
        n--
    }
    
    return node
 }

封装滚动条距离

function getScrollOffset() {
    if (window.pageXOffset) {
        return {
            left: window.pageXOffset,
            top: window.pageYOffset
        }
     } else {
         return {
             left: document.body.scrollLeft + document.documentElement.scrollLeft,
             top: document.body.scrollTop + document.documentElement.scrollTop
         }
     }
}

封装文档高度

function getScrollSize() {
    if (document.body.scrollHeight) {
        return {
            width: document.body.scrollWidth,
            height: document.body.scrollHeight
        } else {
            return {
              width: document.documentElement.scrollWidth,
              height: document.documentElement.scrollHeight
            }
        }
    }
}

封装可视区域

function getViewportSize() {
  if (window.innerWidth) {
    /* 常规判断浏览器可视尺寸 */
    return {
      widthwindow.innerWidth,
      heightwindow.innerHeight,
    };
  } else {
    if (document.comatMode === 'BackCompat') {
      /* 怪异模式下浏览器的可是尺寸document.body.clientWidth; */
      return {
        widthdocument.body.clientWidth,
        heightdocument.body.clientHeight,
      };
    } else {
      /* 标准模式下的浏览器可是尺寸 */
      return {
        widthdocument.documentElement.clientWidth,
        heightdocument.documentElement.clientHeight,
      };
    }
  }
}

找到元素相对于文档定位

function getElemDocPostion(el) {
  /*  offsetLeft/offsetTop 相对于有定位的元素父级来计算,直到找到有定位的父级, 如果找不到就是body offsetParent 找有定位的父级, 返回节点元素, 如果没有, 就返回body */
  var parent = el.offsetParent,
    offsetLeft = el.offsetLeft,
    offsetTop = el.offsetTop
  while (parent) {
    /* 相对文档定位-> 元素相对父级元素位置 + 父级元素相对父级元素位置 + ... */
    offsetLeft += parent.offsetLeft
    offsetTop += parent.offsetTop
    parent = parent.offsetParent
  }
  return {
    left: offsetLeft,
    top: offsetTop,
  }
}

访问元素计算样式

function getstyles(elem, prop) {
  /* getComputedStyle 读取的样式是最终样式,包括了内联样式、嵌入样式和外部样式。 */
  if (window.getComputedStyle) {
    if (prop) {
      return window.getComputedStyle(elem, null)[prop]
    } else {
      return window.getComputedStyle(elem, null)
    }
  } else {
    /* IE8及以下不支持getComputedStyle, 需要使用elem.currentStyle */
    if (prop) {
      return elem.currentStyle[prop]
    } else {
      return elem.currentStyle
    }
  }
}

增加事件绑定

function addEvent(el, typefn) {
  if (el.addEventListener) {
    el.addEventListener(typefnfalse)
  } else if (el.attachEvent) {
    el.attachEvent('on' + typefunction () {
      /* 指向fn函数本身 */
      fn.call(el)
    })
  } else {
    /* 句柄写法的兼容性是最好的,如果都不兼容了就要用句柄了 */
    el['on' + type] = fn
  }
}

移除事件绑定

function removeEvent(el, typefn) {
  if (el.addEventListener) {
    el.removeEventListener(typefnfalse)
  } else if (el.attachEvent) {
    el.detachEvent('on' + typefn)
  } else {
    /* 句柄写法的兼容性是最好的,如果都不兼容了就要用句柄了 */
    el['on' + type] = null
  }
}

取消事件冒泡

function cancelBubble(e) {
  var e = e || window.event
  if (e.stopPropagation) {
    /* event.stopPropagation :阻止捕获和冒泡阶段中当前事件的进一步传播。IE8及以下不支持*/
    e.stopPropagation()
  } else {
    /* IE8及以下需要使用cancelBubble */
    e.cancelBubble = true
  }
}

阻止默认事件

function preventDefaultEvent(e) {
  var e = e || window.event
  /* e.preventDefault()IE9不兼容,IE9以下需要使用e.returnValue = false */
  if (e.preventDefault) {
    event.preventDefault()
  } else {
    event.returnValue = false
  }
}

计算鼠标至文档的距离

function pagePos(e) {
  var sLeft = getScrollOffset().left,
    sTop = getScrollOffset().top,
    /* document.documentElement.clientLeft在某些浏览器中可能有值(IE8),不过通常来说是undefined或0 */
    cLeft = document.documentElement.clientLeft || 0,
    cTop = document.documentElement.clientTop || 0;

  return {
    X: e.clientX + sLeft - cLeft,
    Y: e.clientY + sTop - cTop,
  }
}

拖拽元素

function elemDrag(elem) {
  var x, y;
  /*
    getstyles(box, 'left'), getstyles(box, 'top') 是元素距离文档的距离
    pagePos(e).X pagePos(e).Y 是鼠标距离文档的距离
    x,y 是鼠标距离元素的距离
    这样可以保证拖拽的时候鼠标在选中元素时候的相对坐标不变
  */
  addEvent(elem, 'mousedown'function (e) {
    var e = e || window.event
    x = pagePos(e).X - parseInt(getstyles(elem, 'left'))
    y = pagePos(e).Y - parseInt(getstyles(elem, 'top'))
    h = parseInt(getstyles(elem, 'height'))
    w = parseInt(getstyles(elem, 'width')
    /* 这里使用document.onmousemove 和 document.onmouseup
      因为在鼠标移动速度过快的时候会移出元素,
      当移出元素的时候, 事件就不在元素上了,函数也就不会继续执行*/
    addEvent(document'mousemove', mouseMove);
    /* 鼠标抬起的时候,取消事件绑定 */
    addEvent(document'mouseup', mouseUp);
  })
  function mouseMove(e) {
    var e = e || window.event,
      eX = pagePos(e).X - x,
      eY = pagePos(e).Y - y
    if (eX <= 0) {
      elem.style.left = '0px'
    } else if (eX >= getViewportSize().width - w) {
      elem.style.right = '0px'
    } else {
      elem.style.left = pagePos(e).X - x + 'px'
    }
    if (eY <= 0) {
      elem.style.top = '0px'
    } else if (eY >= getViewportSize().height - h) {
      elem.style.bottom = '0px'
    } else {
      elem.style.top = pagePos(e).Y - y + 'px'
    }
  }
  function mouseUp(e) {
    var e = e || window.event
    removeEvent(document'mousemove', mouseMove)
    removeEvent(document'mouseup', mouseUp)
  }
}