Skip to content

Commit

Permalink
only use click for v-model checkbox/radio in Chrome (fix #4796, #4896)
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 22, 2017
1 parent 29f6902 commit 8e854a9
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/core/util/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const isEdge = UA && UA.indexOf('edge/') > 0
export const isAndroid = UA && UA.indexOf('android') > 0
export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge

// this needs to be lazy-evaled because vue may be required before
// vue-server-renderer can set VUE_ENV
Expand Down
7 changes: 2 additions & 5 deletions src/entries/web-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import config from 'core/config'
import { patch } from 'web/runtime/patch'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser, isEdge } from 'core/util/index'
import { devtools, inBrowser, isChrome } from 'core/util/index'
import platformDirectives from 'web/runtime/directives/index'
import platformComponents from 'web/runtime/components/index'

Expand Down Expand Up @@ -45,10 +45,7 @@ setTimeout(() => {
if (config.devtools) {
if (devtools) {
devtools.emit('init', Vue)
} else if (
process.env.NODE_ENV !== 'production' &&
inBrowser && !isEdge && /Chrome\/\d+/.test(window.navigator.userAgent)
) {
} else if (process.env.NODE_ENV !== 'production' && isChrome) {
console[console.info ? 'info' : 'log'](
'Download the Vue Devtools extension for a better development experience:\n' +
'https://github.com/vuejs/vue-devtools'
Expand Down
16 changes: 12 additions & 4 deletions src/platforms/web/compiler/directives/model.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/* @flow */

import config from 'core/config'
import { isIE } from 'core/util/env'
import { addHandler, addProp, getBindingAttr } from 'compiler/helpers'
import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'

let warn

// in some cases, the event used has to be determined at runtime
// so we used some reserved tokens during compile.
export const RANGE_TOKEN = '__r'
export const CHECKBOX_RADIO_TOKEN = '__c'

export default function model (
el: ASTElement,
dir: ASTDirective,
Expand Down Expand Up @@ -86,7 +90,7 @@ function genCheckboxModel (
: `:_q(${value},${trueValueBinding})`
)
)
addHandler(el, 'click',
addHandler(el, CHECKBOX_RADIO_TOKEN,
`var $$a=${value},` +
'$$el=$event.target,' +
`$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` +
Expand Down Expand Up @@ -117,7 +121,7 @@ function genRadioModel (
let valueBinding = getBindingAttr(el, 'value') || 'null'
valueBinding = number ? `_n(${valueBinding})` : valueBinding
addProp(el, 'checked', `_q(${value},${valueBinding})`)
addHandler(el, 'click', genAssignmentCode(value, valueBinding), null, true)
addHandler(el, CHECKBOX_RADIO_TOKEN, genAssignmentCode(value, valueBinding), null, true)
}

function genSelect (
Expand Down Expand Up @@ -162,8 +166,12 @@ function genDefaultModel (
): ?boolean {
const type = el.attrsMap.type
const { lazy, number, trim } = modifiers || {}
const event = lazy || (isIE && type === 'range') ? 'change' : 'input'
const needCompositionGuard = !lazy && type !== 'range'
const event = lazy
? 'change'
: type === 'range'
? RANGE_TOKEN
: 'input'

let valueExpression = '$event.target.value'
if (trim) {
Expand Down
23 changes: 23 additions & 0 deletions src/platforms/web/runtime/modules/events.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
/* @flow */

import { isChrome, isIE } from 'core/util/env'
import { updateListeners } from 'core/vdom/helpers/index'
import { RANGE_TOKEN, CHECKBOX_RADIO_TOKEN } from 'web/compiler/directives/model'

// normalize v-model event tokens that can only be determined at runtime.
// it's important to place the event as the first in the array because
// the whole point is ensuring the v-model callback gets called before
// user-attached handlers.
function normalizeEvents (on) {
let event
if (on[RANGE_TOKEN]) {
// IE input[type=range] only supports `change` event
event = isIE ? 'change' : 'input'
on[event] = [].concat(on[RANGE_TOKEN], on[event] || [])
delete on[RANGE_TOKEN]
}
if (on[CHECKBOX_RADIO_TOKEN]) {
// Chrome fires microtasks in between click/change, leads to #4521
event = isChrome ? 'click' : 'change'
on[event] = [].concat(on[CHECKBOX_RADIO_TOKEN], on[event] || [])
delete on[CHECKBOX_RADIO_TOKEN]
}
}

let target: HTMLElement

Expand Down Expand Up @@ -41,6 +63,7 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
const on = vnode.data.on || {}
const oldOn = oldVnode.data.on || {}
target = vnode.elm
normalizeEvents(on)
updateListeners(on, oldOn, add, remove, vnode.context)
}

Expand Down

0 comments on commit 8e854a9

Please sign in to comment.