| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- /**
- * Not type checking this file because flow doesn't like attaching
- * properties to Elements.
- */
- import { isTextInputType } from 'web/util/element'
- import { looseEqual, looseIndexOf } from 'shared/util'
- import { mergeVNodeHook } from 'core/vdom/helpers/index'
- import { warn, isIE9, isIE, isEdge } from 'core/util/index'
- /* istanbul ignore if */
- if (isIE9) {
- // http://www.matts411.com/post/internet-explorer-9-oninput/
- document.addEventListener('selectionchange', () => {
- const el = document.activeElement
- if (el && el.vmodel) {
- trigger(el, 'input')
- }
- })
- }
- const directive = {
- inserted (el, binding, vnode, oldVnode) {
- if (vnode.tag === 'select') {
- // #6903
- if (oldVnode.elm && !oldVnode.elm._vOptions) {
- mergeVNodeHook(vnode, 'postpatch', () => {
- directive.componentUpdated(el, binding, vnode)
- })
- } else {
- setSelected(el, binding, vnode.context)
- }
- el._vOptions = [].map.call(el.options, getValue)
- } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
- el._vModifiers = binding.modifiers
- if (!binding.modifiers.lazy) {
- el.addEventListener('compositionstart', onCompositionStart)
- el.addEventListener('compositionend', onCompositionEnd)
- // Safari < 10.2 & UIWebView doesn't fire compositionend when
- // switching focus before confirming composition choice
- // this also fixes the issue where some browsers e.g. iOS Chrome
- // fires "change" instead of "input" on autocomplete.
- el.addEventListener('change', onCompositionEnd)
- /* istanbul ignore if */
- if (isIE9) {
- el.vmodel = true
- }
- }
- }
- },
- componentUpdated (el, binding, vnode) {
- if (vnode.tag === 'select') {
- setSelected(el, binding, vnode.context)
- // in case the options rendered by v-for have changed,
- // it's possible that the value is out-of-sync with the rendered options.
- // detect such cases and filter out values that no longer has a matching
- // option in the DOM.
- const prevOptions = el._vOptions
- const curOptions = el._vOptions = [].map.call(el.options, getValue)
- if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
- // trigger change event if
- // no matching option found for at least one value
- const needReset = el.multiple
- ? binding.value.some(v => hasNoMatchingOption(v, curOptions))
- : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions)
- if (needReset) {
- trigger(el, 'change')
- }
- }
- }
- }
- }
- function setSelected (el, binding, vm) {
- actuallySetSelected(el, binding, vm)
- /* istanbul ignore if */
- if (isIE || isEdge) {
- setTimeout(() => {
- actuallySetSelected(el, binding, vm)
- }, 0)
- }
- }
- function actuallySetSelected (el, binding, vm) {
- const value = binding.value
- const isMultiple = el.multiple
- if (isMultiple && !Array.isArray(value)) {
- process.env.NODE_ENV !== 'production' && warn(
- `<select multiple v-model="${binding.expression}"> ` +
- `expects an Array value for its binding, but got ${
- Object.prototype.toString.call(value).slice(8, -1)
- }`,
- vm
- )
- return
- }
- let selected, option
- for (let i = 0, l = el.options.length; i < l; i++) {
- option = el.options[i]
- if (isMultiple) {
- selected = looseIndexOf(value, getValue(option)) > -1
- if (option.selected !== selected) {
- option.selected = selected
- }
- } else {
- if (looseEqual(getValue(option), value)) {
- if (el.selectedIndex !== i) {
- el.selectedIndex = i
- }
- return
- }
- }
- }
- if (!isMultiple) {
- el.selectedIndex = -1
- }
- }
- function hasNoMatchingOption (value, options) {
- return options.every(o => !looseEqual(o, value))
- }
- function getValue (option) {
- return '_value' in option
- ? option._value
- : option.value
- }
- function onCompositionStart (e) {
- e.target.composing = true
- }
- function onCompositionEnd (e) {
- // prevent triggering an input event for no reason
- if (!e.target.composing) return
- e.target.composing = false
- trigger(e.target, 'input')
- }
- function trigger (el, type) {
- const e = document.createEvent('HTMLEvents')
- e.initEvent(type, true, true)
- el.dispatchEvent(e)
- }
- export default directive
|