dom-props.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. // skip the update if old and new VDOM state is the same.
  37. // the only exception is `value` where the DOM value may be temporarily
  38. // out of sync with VDOM state due to focus, composition and modifiers.
  39. // This also covers #4521 by skipping the unnecesarry `checked` update.
  40. if (key !== 'value' && cur === oldProps[key]) {
  41. continue
  42. }
  43. if (key === 'value') {
  44. // store value as _value as well since
  45. // non-string values will be stringified
  46. elm._value = cur
  47. // avoid resetting cursor position when value is the same
  48. const strCur = isUndef(cur) ? '' : String(cur)
  49. if (shouldUpdateValue(elm, strCur)) {
  50. elm.value = strCur
  51. }
  52. } else if (key === 'innerHTML' && isSVG(elm.tagName) && isUndef(elm.innerHTML)) {
  53. // IE doesn't support innerHTML for SVG elements
  54. svgContainer = svgContainer || document.createElement('div')
  55. svgContainer.innerHTML = `<svg>${cur}</svg>`
  56. const svg = svgContainer.firstChild
  57. while (elm.firstChild) {
  58. elm.removeChild(elm.firstChild)
  59. }
  60. while (svg.firstChild) {
  61. elm.appendChild(svg.firstChild)
  62. }
  63. } else {
  64. elm[key] = cur
  65. }
  66. }
  67. }
  68. // check platforms/web/util/attrs.js acceptValue
  69. type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement;
  70. function shouldUpdateValue (elm: acceptValueElm, checkVal: string): boolean {
  71. return (!elm.composing && (
  72. elm.tagName === 'OPTION' ||
  73. isNotInFocusAndDirty(elm, checkVal) ||
  74. isDirtyWithModifiers(elm, checkVal)
  75. ))
  76. }
  77. function isNotInFocusAndDirty (elm: acceptValueElm, checkVal: string): boolean {
  78. // return true when textbox (.number and .trim) loses focus and its value is
  79. // not equal to the updated value
  80. let notInFocus = true
  81. // #6157
  82. // work around IE bug when accessing document.activeElement in an iframe
  83. try { notInFocus = document.activeElement !== elm } catch (e) {}
  84. return notInFocus && elm.value !== checkVal
  85. }
  86. function isDirtyWithModifiers (elm: any, newVal: string): boolean {
  87. const value = elm.value
  88. const modifiers = elm._vModifiers // injected by v-model runtime
  89. if (isDef(modifiers)) {
  90. if (modifiers.number) {
  91. return toNumber(value) !== toNumber(newVal)
  92. }
  93. if (modifiers.trim) {
  94. return value.trim() !== newVal.trim()
  95. }
  96. }
  97. return value !== newVal
  98. }
  99. export default {
  100. create: updateDOMProps,
  101. update: updateDOMProps
  102. }