8d91aa0bf3ed1195116ddf6258e96d5dd8b7bdf8953a74db9f2c1f8bf907001a3063180e8726188817342e36da0cd2c1483fbd636f95e3471d8d329c42ef10 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /* @flow */
  2. import { isIE, isIE9, isEdge } from 'core/util/env'
  3. import {
  4. extend,
  5. isDef,
  6. isUndef
  7. } from 'shared/util'
  8. import {
  9. isXlink,
  10. xlinkNS,
  11. getXlinkProp,
  12. isBooleanAttr,
  13. isEnumeratedAttr,
  14. isFalsyAttrValue,
  15. convertEnumeratedValue
  16. } from 'web/util/index'
  17. function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  18. const opts = vnode.componentOptions
  19. if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
  20. return
  21. }
  22. if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
  23. return
  24. }
  25. let key, cur, old
  26. const elm = vnode.elm
  27. const oldAttrs = oldVnode.data.attrs || {}
  28. let attrs: any = vnode.data.attrs || {}
  29. // clone observed objects, as the user probably wants to mutate it
  30. if (isDef(attrs.__ob__)) {
  31. attrs = vnode.data.attrs = extend({}, attrs)
  32. }
  33. for (key in attrs) {
  34. cur = attrs[key]
  35. old = oldAttrs[key]
  36. if (old !== cur) {
  37. setAttr(elm, key, cur)
  38. }
  39. }
  40. // #4391: in IE9, setting type can reset value for input[type=radio]
  41. // #6666: IE/Edge forces progress value down to 1 before setting a max
  42. /* istanbul ignore if */
  43. if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {
  44. setAttr(elm, 'value', attrs.value)
  45. }
  46. for (key in oldAttrs) {
  47. if (isUndef(attrs[key])) {
  48. if (isXlink(key)) {
  49. elm.removeAttributeNS(xlinkNS, getXlinkProp(key))
  50. } else if (!isEnumeratedAttr(key)) {
  51. elm.removeAttribute(key)
  52. }
  53. }
  54. }
  55. }
  56. function setAttr (el: Element, key: string, value: any) {
  57. if (el.tagName.indexOf('-') > -1) {
  58. baseSetAttr(el, key, value)
  59. } else if (isBooleanAttr(key)) {
  60. // set attribute for blank value
  61. // e.g. <option disabled>Select one</option>
  62. if (isFalsyAttrValue(value)) {
  63. el.removeAttribute(key)
  64. } else {
  65. // technically allowfullscreen is a boolean attribute for <iframe>,
  66. // but Flash expects a value of "true" when used on <embed> tag
  67. value = key === 'allowfullscreen' && el.tagName === 'EMBED'
  68. ? 'true'
  69. : key
  70. el.setAttribute(key, value)
  71. }
  72. } else if (isEnumeratedAttr(key)) {
  73. el.setAttribute(key, convertEnumeratedValue(key, value))
  74. } else if (isXlink(key)) {
  75. if (isFalsyAttrValue(value)) {
  76. el.removeAttributeNS(xlinkNS, getXlinkProp(key))
  77. } else {
  78. el.setAttributeNS(xlinkNS, key, value)
  79. }
  80. } else {
  81. baseSetAttr(el, key, value)
  82. }
  83. }
  84. function baseSetAttr (el, key, value) {
  85. if (isFalsyAttrValue(value)) {
  86. el.removeAttribute(key)
  87. } else {
  88. // #7138: IE10 & 11 fires input event when setting placeholder on
  89. // <textarea>... block the first input event and remove the blocker
  90. // immediately.
  91. /* istanbul ignore if */
  92. if (
  93. isIE && !isIE9 &&
  94. el.tagName === 'TEXTAREA' &&
  95. key === 'placeholder' && value !== '' && !el.__ieph
  96. ) {
  97. const blocker = e => {
  98. e.stopImmediatePropagation()
  99. el.removeEventListener('input', blocker)
  100. }
  101. el.addEventListener('input', blocker)
  102. // $flow-disable-line
  103. el.__ieph = true /* IE placeholder patched */
  104. }
  105. el.setAttribute(key, value)
  106. }
  107. }
  108. export default {
  109. create: updateAttrs,
  110. update: updateAttrs
  111. }