dom-props.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /* @flow */
  2. import { isDef, isUndef, extend, toNumber } from 'shared/util'
  3. import { isSVG } from 'web/util/index'
  4. let svgContainer
  5. function updateDOMProps (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  6. if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) {
  7. return
  8. }
  9. let key, cur
  10. const elm: any = vnode.elm
  11. const oldProps = oldVnode.data.domProps || {}
  12. let props = vnode.data.domProps || {}
  13. // clone observed objects, as the user probably wants to mutate it
  14. if (isDef(props.__ob__)) {
  15. props = vnode.data.domProps = extend({}, props)
  16. }
  17. for (key in oldProps) {
  18. if (isUndef(props[key])) {
  19. elm[key] = ''
  20. }
  21. }
  22. for (key in props) {
  23. cur = props[key]
  24. // ignore children if the node has textContent or innerHTML,
  25. // as these will throw away existing DOM nodes and cause removal errors
  26. // on subsequent patches (#3360)
  27. if (key === 'textContent' || key === 'innerHTML') {
  28. if (vnode.children) vnode.children.length = 0
  29. if (cur === oldProps[key]) continue
  30. // #6601 work around Chrome version <= 55 bug where single textNode
  31. // replaced by innerHTML/textContent retains its parentNode property
  32. if (elm.childNodes.length === 1) {
  33. elm.removeChild(elm.childNodes[0])
  34. }
  35. }
  36. if (key === 'value') {
  37. // store value as _value as well since
  38. // non-string values will be stringified
  39. elm._value = cur
  40. // avoid resetting cursor position when value is the same
  41. const strCur = isUndef(cur) ? '' : String(cur)
  42. if (shouldUpdateValue(elm, strCur)) {
  43. elm.value = strCur
  44. }
  45. } else if (key === 'innerHTML' && isSVG(elm.tagName) && isUndef(elm.innerHTML)) {
  46. // IE doesn't support innerHTML for SVG elements
  47. svgContainer = svgContainer || document.createElement('div')
  48. svgContainer.innerHTML = `<svg>${cur}</svg>`
  49. const svg = svgContainer.firstChild
  50. while (elm.firstChild) {
  51. elm.removeChild(elm.firstChild)
  52. }
  53. while (svg.firstChild) {
  54. elm.appendChild(svg.firstChild)
  55. }
  56. } else if (
  57. // skip the update if old and new VDOM state is the same.
  58. // `value` is handled separately because the DOM value may be temporarily
  59. // out of sync with VDOM state due to focus, composition and modifiers.
  60. // This #4521 by skipping the unnecesarry `checked` update.
  61. cur !== oldProps[key]
  62. ) {
  63. elm[key] = cur
  64. }
  65. }
  66. }
  67. // check platforms/web/util/attrs.js acceptValue
  68. type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement;
  69. function shouldUpdateValue (elm: acceptValueElm, checkVal: string): boolean {
  70. return (!elm.composing && (
  71. elm.tagName === 'OPTION' ||
  72. isNotInFocusAndDirty(elm, checkVal) ||
  73. isDirtyWithModifiers(elm, checkVal)
  74. ))
  75. }
  76. function isNotInFocusAndDirty (elm: acceptValueElm, checkVal: string): boolean {
  77. // return true when textbox (.number and .trim) loses focus and its value is
  78. // not equal to the updated value
  79. let notInFocus = true
  80. // #6157
  81. // work around IE bug when accessing document.activeElement in an iframe
  82. try { notInFocus = document.activeElement !== elm } catch (e) {}
  83. return notInFocus && elm.value !== checkVal
  84. }
  85. function isDirtyWithModifiers (elm: any, newVal: string): boolean {
  86. const value = elm.value
  87. const modifiers = elm._vModifiers // injected by v-model runtime
  88. if (isDef(modifiers)) {
  89. if (modifiers.number) {
  90. return toNumber(value) !== toNumber(newVal)
  91. }
  92. if (modifiers.trim) {
  93. return value.trim() !== newVal.trim()
  94. }
  95. }
  96. return value !== newVal
  97. }
  98. export default {
  99. create: updateDOMProps,
  100. update: updateDOMProps
  101. }