Input.valueAsNumber & valueAsDate

参考 (opens in a new tab)

浏览器支持度很高,全支持了

通常我们处理 input 输入的数字都是通过 string 转成 number

export function NumberInput() {
  const [number, setNumber] = useState(0);
 
  return (
    <input
      type="number"
      value={number}
      onChange={(e) => {
        const num = parseFloat(e.target.value);
        setNumber(num);
      }}
    />
  );
}

但其实浏览器提供了一个自动转换的属性 valuesAsNumber 能够省去我们转换 number 的这一步

      onChange={(e) => {
        // ✅
        const num = e.target.valueAsNumber
        setNumber(num)
      }}

注意:这个属性的类型永远都是 number,但可能会是 NaN

typeof NaN; // 'number'

所以需要多判断一步 !isNaN(xx)

同样有 valueAsDate 是浏览器自动转换好的 Date 对象

export function DateInput() {
  const [date, setDate] = useState(null);
 
  return (
    <input
      type="date"
      value={date}
      onChange={(e) => {
        // ✅
        const date = e.target.valueAsDate;
        setDate(date);
      }}
    />
  );
}

如果 input 是空,则会拿到 null

const date = myDateInput.valueAsDate;
if (date) {
  // We've got a date!
}

Node.appendChild & ParentNode.append

两者都是在元素节点后 append 新的节点

Node.appendChildParentNode.append
功能如果这个节点已经有父节点,则会先移除,后加到新的父节点后插入一组 Node 或者 DOMString
参数个数1n
参数类型Node onlyNode 或者 DOMString(UTF-16 String 就是 js 的 String)
返回返回被插入的 Node 对象None

IE 不支持 append,appendChild 都支持的

Node.cloneNode()

let newClone = node.cloneNode([deep]); // node.cloneNode(true)

克隆一个新的节点对象(尚未加入 document)

deep 如果为 true,会拷贝节点的完整子树以及其中的文本,对空标签没有什么作用(比如 img 和 input);false 的时候仅 Node 被拷贝

注意:clone 节点可能会导致两个 ID 重复

Node.nodeType

ConstantValueDescription
Node.ELEMENT_NODE1An Element (opens in a new tab) node like ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p) or [ (opens in a new tab).
Node.TEXT_NODE3The actual Text (opens in a new tab) inside an Element (opens in a new tab) or Attr (opens in a new tab).
Node.CDATA_SECTION_NODE4A CDATASection (opens in a new tab), such as ``.
Node.PROCESSING_INSTRUCTION_NODE7A ProcessingInstruction (opens in a new tab) of an XML document, such as ``.
Node.COMMENT_NODE8A Comment (opens in a new tab) node, such as ``.
Node.DOCUMENT_NODE9A Document (opens in a new tab) node.
Node.DOCUMENT_TYPE_NODE10A DocumentType (opens in a new tab) node, such as ``.
Node.DOCUMENT_FRAGMENT_NODE11A DocumentFragment (opens in a new tab) node.

判断节点是否在 document 上(TODO 改成 es6)

/**
 * 判断元素是否还在doc上
 * @param  {[type]} node [description]
 * @return {[type]}      [description]
 */
function inDoc(node) {
  if (!node) return false;
  var doc = node.ownerDocument.documentElement;
  var parent = node.parentNode;
  return (
    doc === node ||
    doc === parent ||
    // 这里判断 节点类型
    !!(parent && parent.nodeType === Node.ELEMENT_NODE && doc.contains(parent))
  );
}

Node.childNodes

返回子节点的 NodeList,这个 API 在抖音面试的时候用到了(求子节点是 input 的所有节点),不得不说这个 API 的名字有点怪

可以通过 hasChildNodes 来判断

注意:childNodes 会返回所有的节点,包括 text,如果只想要 elements,可以用 ParentNode.children

ParentNode.children

Node.nodeName

好像和 tagName 是一样的值,标签都会返回大写(DIV, INPUT...,Read more details on nodeName case sensitivity in different browsers (opens in a new tab).)

tagName 的区别:Bear in mind, however, that nodeName will return "#text" for text nodes while tagName will return undefined.

Element.getBoundingClientRect()

顾名思义,获取边框长方形,不过这个 client 是什么意思?哦哦客户端上的,所以是实际在 viewport 的大小,并且是实时的

众所周知,一个盒子的宽和高是 css 的 width/height + padding + border-width,这个是 W3C(content)盒子,如果是 box-sizing: border-box,就只看 width/height

domRect = element.getBoundingClientRect();
// 返回例如下 实时定位
// bottom: -411
// height: 0
// left: -4
// right: 1016
// top: -411
// width: 1020
// x: -4
// y: -411

可见:

  • right - left = width + padding
  • bottom - top = height + padding

HTMLElement.offsetHeight/Top/Left/Width

返回与最近的相对位置(position: relative)的父元素 Top 的偏差值

滚轮相关

document.documentElement.scrollTop

滚轮对象,可以通过修改scrollTop来进行滚动,其他属性是只读的,先不做了解

获取滚轮高度的值可以这样

const scrollTop =
  window.pageYOffset ||
  document.documentElement.scrollTop ||
  document.body.scrollTop;

修改滚轮高度可以这样

const scrollTop =
  window.pageYOffset ||
  document.documentElement.scrollTop ||
  document.body.scrollTop;
const bodyOrHtml = () => {
  if ("scrollingElement" in document) {
    return document.scrollingElement;
  }
  if (window.navigator.userAgent.indexOf("WebKit") != -1) {
    // navigator为浏览器自带对象
    return document.body;
  }
  return document.documentElement;
};
// 定义一个 滚动函数
let start = 0;
const during = 15;
const _run = () => {
  start++;
  // 平滑滚动
  let top = easeInOut(start, scrollTop, offsetTop - scrollTop, during);
  bodyOrHtml().scrollTop = top;
  // 每一帧渲染的时候去滚动
  if (start < during) window.requestAnimationFrame(_run);
};

window.scrollY

滚轮滑动的位置

window.pageYOffset === window.scrollY; // always true
// make sure and go down to the second page
if (window.scrollY) {
  window.scroll(0, 0); // reset the scroll position to the top left of the document.
}
 
window.scrollByPages(1);