vue2/src/util/dom.js

297 lines
5.8 KiB
JavaScript
Raw Normal View History

var _ = require('./index')
2014-08-12 20:59:49 +08:00
var config = require('../config')
/**
* Query an element selector if it's not an element already.
*
* @param {String|Element} el
* @return {Element}
*/
exports.query = function (el) {
if (typeof el === 'string') {
var selector = el
el = document.querySelector(el)
if (!el) {
process.env.NODE_ENV !== 'production' && _.warn(
'Cannot find element: ' + selector
)
}
}
return el
}
2014-08-23 06:27:06 +08:00
/**
* Check if a node is in the document.
* Note: document.documentElement.contains should work here
* but always returns false for comment nodes in phantomjs,
* making unit tests difficult. This is fixed byy doing the
* contains() check on the node's parentNode instead of
* the node itself.
2014-08-23 06:27:06 +08:00
*
* @param {Node} node
* @return {Boolean}
*/
exports.inDoc = function (node) {
2015-06-03 01:44:11 +08:00
var doc = document.documentElement
var parent = node && node.parentNode
return doc === node ||
doc === parent ||
!!(parent && parent.nodeType === 1 && (doc.contains(parent)))
2014-08-23 06:27:06 +08:00
}
2014-08-12 20:59:49 +08:00
/**
* Extract an attribute from a node.
*
* @param {Node} node
* @param {String} attr
*/
exports.attr = function (node, attr) {
2014-08-13 08:09:37 +08:00
attr = config.prefix + attr
2014-08-12 20:59:49 +08:00
var val = node.getAttribute(attr)
2014-08-13 13:30:33 +08:00
if (val !== null) {
node.removeAttribute(attr)
}
2014-08-12 20:59:49 +08:00
return val
}
/**
* Get an attribute with colon or bind- prefix.
*
* @param {Node} node
* @param {String} name
* @return {String|null}
*/
exports.getBindAttr = function (node, name) {
var attr = ':' + name
var val = node.getAttribute(attr)
if (val === null) {
attr = 'bind-' + name
val = node.getAttribute(attr)
}
if (val !== null) {
node.removeAttribute(attr)
}
return val
}
2014-07-27 03:56:32 +08:00
/**
* Insert el before target
*
* @param {Element} el
* @param {Element} target
2014-07-27 03:56:32 +08:00
*/
exports.before = function (el, target) {
target.parentNode.insertBefore(el, target)
}
/**
* Insert el after target
*
* @param {Element} el
* @param {Element} target
2014-07-27 03:56:32 +08:00
*/
exports.after = function (el, target) {
if (target.nextSibling) {
exports.before(el, target.nextSibling)
} else {
target.parentNode.appendChild(el)
}
}
/**
* Remove el from DOM
*
* @param {Element} el
*/
exports.remove = function (el) {
el.parentNode.removeChild(el)
}
/**
* Prepend el to target
*
* @param {Element} el
* @param {Element} target
2014-07-27 03:56:32 +08:00
*/
exports.prepend = function (el, target) {
if (target.firstChild) {
exports.before(el, target.firstChild)
} else {
target.appendChild(el)
}
}
2014-08-21 06:46:42 +08:00
/**
* Replace target with el
*
* @param {Element} target
* @param {Element} el
*/
exports.replace = function (target, el) {
var parent = target.parentNode
2014-09-11 11:36:15 +08:00
if (parent) {
parent.replaceChild(el, target)
}
2014-08-21 06:46:42 +08:00
}
2014-08-31 11:08:00 +08:00
/**
* Add event listener shorthand.
*
* @param {Element} el
* @param {String} event
* @param {Function} cb
*/
exports.on = function (el, event, cb) {
el.addEventListener(event, cb)
}
/**
* Remove event listener shorthand.
*
* @param {Element} el
* @param {String} event
* @param {Function} cb
*/
exports.off = function (el, event, cb) {
el.removeEventListener(event, cb)
2014-09-14 07:09:26 +08:00
}
/**
* Add class with compatibility for IE & SVG
2014-09-14 07:09:26 +08:00
*
* @param {Element} el
* @param {Strong} cls
*/
exports.addClass = function (el, cls) {
if (el.classList) {
el.classList.add(cls)
} else {
var cur = ' ' + (el.getAttribute('class') || '') + ' '
if (cur.indexOf(' ' + cls + ' ') < 0) {
el.setAttribute('class', (cur + cls).trim())
}
2014-09-14 07:09:26 +08:00
}
}
/**
* Remove class with compatibility for IE & SVG
2014-09-14 07:09:26 +08:00
*
* @param {Element} el
* @param {Strong} cls
*/
exports.removeClass = function (el, cls) {
if (el.classList) {
el.classList.remove(cls)
} else {
var cur = ' ' + (el.getAttribute('class') || '') + ' '
var tar = ' ' + cls + ' '
while (cur.indexOf(tar) >= 0) {
cur = cur.replace(tar, ' ')
}
el.setAttribute('class', cur.trim())
2014-09-14 07:09:26 +08:00
}
2015-08-29 23:32:49 +08:00
if (!el.className) {
el.removeAttribute('class')
}
2014-12-02 08:36:59 +08:00
}
/**
* Extract raw content inside an element into a temporary
* container div
*
* @param {Element} el
* @param {Boolean} asFragment
2014-12-02 08:36:59 +08:00
* @return {Element}
*/
exports.extractContent = function (el, asFragment) {
2014-12-02 08:36:59 +08:00
var child
var rawContent
/* istanbul ignore if */
if (
2015-06-10 22:39:48 +08:00
exports.isTemplate(el) &&
el.content instanceof DocumentFragment
) {
el = el.content
}
2014-12-02 08:36:59 +08:00
if (el.hasChildNodes()) {
exports.trimNode(el)
rawContent = asFragment
? document.createDocumentFragment()
: document.createElement('div')
2015-07-02 20:59:11 +08:00
/* eslint-disable no-cond-assign */
2014-12-02 08:36:59 +08:00
while (child = el.firstChild) {
2015-07-02 20:59:11 +08:00
/* eslint-enable no-cond-assign */
2014-12-02 08:36:59 +08:00
rawContent.appendChild(child)
}
}
return rawContent
}
2015-06-10 22:39:48 +08:00
/**
* Trim possible empty head/tail textNodes inside a parent.
*
* @param {Node} node
*/
exports.trimNode = function (node) {
trim(node, node.firstChild)
trim(node, node.lastChild)
}
function trim (parent, node) {
if (node && node.nodeType === 3 && !node.data.trim()) {
parent.removeChild(node)
}
}
2015-06-10 22:39:48 +08:00
/**
* Check if an element is a template tag.
* Note if the template appears inside an SVG its tagName
* will be in lowercase.
*
* @param {Element} el
*/
exports.isTemplate = function (el) {
return el.tagName &&
el.tagName.toLowerCase() === 'template'
}
/**
* Create an "anchor" for performing dom insertion/removals.
* This is used in a number of scenarios:
* - fragment instance
* - v-html
* - v-if
2015-09-02 22:15:30 +08:00
* - v-for
* - component
*
* @param {String} content
* @param {Boolean} persist - IE trashes empty textNodes on
* cloneNode(true), so in certain
* cases the anchor needs to be
* non-empty to be persisted in
* templates.
* @return {Comment|Text}
*/
exports.createAnchor = function (content, persist) {
return config.debug
? document.createComment(content)
: document.createTextNode(persist ? ' ' : '')
}