JavaScript 与 jQuery

262 阅读2分钟

jQuery常用功能:

  • 在 DOM 树中进行查询
  • 修改 DOM 树及 DOM 相关操作
  • 事件处理
  • Ajax
  • Deferred 和 Promise
  • 对象和数组处理
  • 跨浏览器兼容

原生JS

查詢和取得 DOM [querySelector querySelectorAll]

// 查询单个
const oneElement = document.querySelector('#foo > div.bar') 
// 查询所有
const allElements = document.querySelectorAll('.bar')
// 在指定元素下查询
const button = allElements.querySelector('button[type="submit"]') 

操作类Class

增加/移除类
// JS
el.classList.add(className);
el.classList.remove(className);

// jQuery
$el.addClass(className);
$el.removeClass(className);

是否有Class
// JS
el.classList.contains(className);

// jQuery
$el.hasClass(className);

切换类
// JS
el.classList.toggle(className);

// jQuery
$el.toggleClass(className);

CSS & Style

1. 获取Style
// JS
const win = el.ownerDocument.defaultView;
win.getComputedStyle(el, null).color;
// jQuery
$el.css('color')

2. 设置Style
// JS
el.style.color = '#fff'
// jQuery
$el.css('color','#fff')

修改DOM

// 建立新的 DOM
const newElement = document.createElement('div')
const newTextNode = document.createTextNode('hello world')

// 复制 DOM
const newElement = oneElement.cloneNode()
element1.appendChild(newElement)

// 移除 DOM,需要參照到親元素
parentElement.removeChild(element1)
// 自己移除自己
element1.parentNode.removeChild(element1)

// 在 element1 后插入一个 element2 
element1.appendChild(element2)

// 在 element1 里 的 element3 之前插入一個 element2
element1.insertBefore(element2, element3)

// 在 element1 里的 element3 「之后」插入一個 element2
element1.insertBefore(element2, element3.nextSibling)

要修改元素的內容,傳統的做法可以用 innerHTML:
oneElement.innerHTML = '<div>
<h1>hello world</h1>
</div>'

更好的做法是使用 DocumentFragment:
const text = document.createTextNode('continue reading...')
const hr = document.createElement('hr')
const fragment = document.createDocumentFragment()
fragment.appendChild(text)
fragment.appendChild(hr)
oneElement.appendChild(fragment)

事件

JavaScript 最重要的就是监听(listen)各種事件触发代码。
我们使用 addEventListener 來监听事件处理。
  oneElement.addEventListener('click', function (event) {
    // do something...
  })

同時监听许多元素时候,通过 event.target 來取得是哪個元素触发的。
  Array.from(allElements).forEach(element => {
    element.addEventListener('change', function (event) {
      console.log(event.target.value)
    })
  })

只想让事件触发一次(jQuery 的 once):
  oneElement.addEventListener('change', function listener(event) {
    console.log(event.type + ' got triggered on ' + this)
    this.removeEventListener('change', listener)
  })

动画

以前习惯用 window.setTimeout() 來做动画,
現在我们有更好更快的 window.requestAnimationFrame() 了。
  const start = window.performance.now()
  const duration = 2000

  window.requestAnimationFrame(function fadeIn (now) {
    const progress = now - start
    oneElement.style.opacity = progress / duration

    if (progress < duration) {
      window.requestAnimationFrame(fadeIn)
    }
  }

Ajax

Fetch API 是用于替换 XMLHttpRequest 处理 ajax 的新标准,Chrome 和 Firefox 均支持,旧浏览器可以使用 polyfills 提供支持。

IE9+ 请使用 github/fetch,IE8+ 请使用 fetch-ie8,JSONP 请使用 fetch-jsonp。

从服务器读取数据并替换匹配元素的内容。
// jQuery
$(selector).load(url, completeCallback)

// Native
fetch(url).then(data => data.text()).then(data => {
  document.querySelector(selector).innerHTML = data
}).then(completeCallback)

封装

我们可以把这些方式全部包在一個 function 里。
就像 jQuery 链式调用一样(chainable)
(例如: $('foo').css({color: 'red'}).on('click', () => {}) )

  const $ = function $(selector, context = document) {
    const elements = Array.from(context.querySelectorAll(selector))
    return {
      elements,

      html (newHtml) {
        this.elements.forEach(element => {
          element.innerHTML = newHtml
        })
        return this
      },

      css (newCss) {
        this.elements.forEach(element => {
          Object.assign(element.style, newCss)
        })
        return this
      },

      on (event, handler, options) {
        this.elements.forEach(element => {
          element.addEventListener(event, handler, options)
        })
        return this
      }
      // etc.
    }
  }

或者用 ES6 的 Class 來包裝:
  class DOM {
    constructor(selector) {
      const elements = document.querySelectorAll(selector)
      this.length = elements.length
      Object.assign(this, elements)
    }

    each(callback) {
      for (let el of Array.from(this)) {
        callback.call(el)
      }
      return this
    }

    addClass(className) {
      return this.each(function () {
        this.classList.add(className)
      })
    }

    removeClass(className) {
      return this.each(function () {
        this.classList.remove(className)
      })
    }

    hasClass(className) {
      return this[0].classList.contains(className)
    }

    on(event, callback) {
      return this.each(function () {
        this.addEventListener(event, callback, false)
      })
    }

    // etc.
  }

参考

  • oui-dom-utils 来做选择器和样式相关
  • oui-dom-events 来做 Event,支持命名空间和事件代理