diff --git a/examples/docs/en-US/input-number.md b/examples/docs/en-US/input-number.md
index 90c1cccbf..aa4d9ab36 100644
--- a/examples/docs/en-US/input-number.md
+++ b/examples/docs/en-US/input-number.md
@@ -163,7 +163,7 @@ Use attribute `size` to set additional sizes with `medium`, `small` or `mini`.
 |debounce| debounce delay when typing, in millisecond | number | — | 300 |
 |controls-position | position of the control buttons | string | right | - |
 |name | same as `name` in native input | string | — | — |
-
+|label | label text | string | — | — |
 ### Events
 
 | Event Name | Description | Parameters |
diff --git a/examples/docs/en-US/input.md b/examples/docs/en-US/input.md
index 1d3c30a7b..a68dd1fa9 100644
--- a/examples/docs/en-US/input.md
+++ b/examples/docs/en-US/input.md
@@ -630,7 +630,7 @@ Search data from server-side.
 |autofocus | same as `autofocus` in native input | boolean | — | false |
 |form | same as `form` in native input | string | — | — |
 | on-icon-click | hook function when clicking on the input icon | function | — | — |
-
+| label | label text | string | — | — |
 ### Input slot
 
 | Name | Description |
@@ -663,7 +663,7 @@ Attribute | Description | Type | Options | Default
 | on-icon-click | hook function when clicking on the input icon | function | — | — |
 | name | same as `name` in native input | string | — | — |
 | select-when-unmatched | whether to emit a `select` event on enter when there is no autocomplete match | boolean | — | false |
-
+| label | label text | string | — | — |
 ### props
 | Attribute | Description | Type | Accepted Values | Default |
 | --------- | ----------------- | ------ | ------ | ------ |
diff --git a/examples/docs/zh-CN/input-number.md b/examples/docs/zh-CN/input-number.md
index f8a252c19..f172efd49 100644
--- a/examples/docs/zh-CN/input-number.md
+++ b/examples/docs/zh-CN/input-number.md
@@ -36,7 +36,7 @@
 :::demo 要使用它,只需要在`el-input-number`元素中使用`v-model`绑定变量即可,变量的初始值即为默认值。
 ```html
 <template>
-  <el-input-number v-model="num1" @change="handleChange" :min="1" :max="10"></el-input-number>
+  <el-input-number v-model="num1" @change="handleChange" :min="1" :max="10" label="描述文字"></el-input-number>
 </template>
 <script>
   export default {
@@ -162,7 +162,7 @@
 | debounce | 输入时的去抖延迟,毫秒 | number | — | 300 |
 | controls-position | 控制按钮位置 | string | right | - |
 | name | 原生属性 | string | — | — |
-
+| label | 输入框关联的label文字 | string | — | — |
 ### Events
 | 事件名称 | 说明 | 回调参数 |
 |---------|--------|---------|
diff --git a/examples/docs/zh-CN/input.md b/examples/docs/zh-CN/input.md
index 0a0d3b448..08b5eee9e 100644
--- a/examples/docs/zh-CN/input.md
+++ b/examples/docs/zh-CN/input.md
@@ -785,7 +785,7 @@ export default {
 | resize | 控制是否能被用户缩放 | string | none, both, horizontal, vertical | — |
 | autofocus | 原生属性,自动获取焦点 | boolean | true, false | false |
 | form | 原生属性 | string | — | — |
-
+| label | 输入框关联的label文字 | string | — | — |
 ### Input slot
 | name | 说明 |
 |------|--------|
@@ -821,7 +821,7 @@ export default {
 | icon          | 输入框尾部图标    | string          | — | — |
 | name | 原生属性 | string | — | — |
 | select-when-unmatched | 在输入没有任何匹配建议的情况下,按下回车是否触发 `select` 事件 | boolean | — | false |
-
+| label | 输入框关联的label文字 | string | — | — |
 ### props
 | 参数     | 说明              | 类型   | 可选值 | 默认值 |
 | -------- | ----------------- | ------ | ------ | ------ |
diff --git a/packages/autocomplete/src/autocomplete-suggestions.vue b/packages/autocomplete/src/autocomplete-suggestions.vue
index 5aeede378..c202c4d25 100644
--- a/packages/autocomplete/src/autocomplete-suggestions.vue
+++ b/packages/autocomplete/src/autocomplete-suggestions.vue
@@ -5,6 +5,7 @@
       class="el-autocomplete-suggestion el-popper"
       :class="{ 'is-loading': parent.loading }"
       :style="{ width: dropdownWidth }"
+      role="region"
     >
       <el-scrollbar
         tag="ul"
@@ -44,7 +45,8 @@
             gpuAcceleration: false
           };
         }
-      }
+      },
+      id: String
     },
 
     methods: {
@@ -62,6 +64,9 @@
     mounted() {
       this.$parent.popperElm = this.popperElm = this.$el;
       this.referenceElm = this.$parent.$refs.input.$refs.input;
+      this.referenceList = this.$el.querySelector('.el-autocomplete-suggestion__list');
+      this.referenceList.setAttribute('role', 'listbox');
+      this.referenceList.setAttribute('id', this.id);
     },
 
     created() {
diff --git a/packages/autocomplete/src/autocomplete.vue b/packages/autocomplete/src/autocomplete.vue
index fd03770db..11d8d3070 100644
--- a/packages/autocomplete/src/autocomplete.vue
+++ b/packages/autocomplete/src/autocomplete.vue
@@ -1,5 +1,12 @@
 <template>
-  <div class="el-autocomplete" v-clickoutside="close">
+  <div
+    class="el-autocomplete"
+    v-clickoutside="close"
+    aria-haspopup="listbox"
+    role="combobox"
+    :aria-expanded="suggestionVisible"
+    :aria-owns="id"
+  >
     <el-input
       ref="input"
       v-bind="$props"
@@ -13,6 +20,7 @@
       @keydown.down.native.prevent="highlight(highlightedIndex + 1)"
       @keydown.enter.native="handleKeyEnter"
       @keydown.native.tab="close"
+      :label="label"
     >
       <template slot="prepend" v-if="$slots.prepend">
         <slot name="prepend"></slot>
@@ -24,12 +32,17 @@
     <el-autocomplete-suggestions
       visible-arrow
       :class="[popperClass ? popperClass : '']"
-      ref="suggestions">
+      ref="suggestions"
+      :id="id">
       <li
         v-for="(item, index) in suggestions"
         :key="index"
         :class="{'highlighted': highlightedIndex === index}"
-        @click="select(item)">
+        @click="select(item)"
+        :id="`${id}-item-${index}`"
+        role="option"
+        :aria-selected="highlightedIndex === index"
+      >
         <slot :item="item">
           {{ item[props.label] }}
         </slot>
@@ -42,6 +55,7 @@
   import Clickoutside from 'element-ui/src/utils/clickoutside';
   import ElAutocompleteSuggestions from './autocomplete-suggestions.vue';
   import Emitter from 'element-ui/src/mixins/emitter';
+  import { generateId } from 'element-ui/src/utils/util';
 
   export default {
     name: 'ElAutocomplete',
@@ -85,7 +99,8 @@
       selectWhenUnmatched: {
         type: Boolean,
         default: false
-      }
+      },
+      label: String
     },
     data() {
       return {
@@ -101,6 +116,9 @@
         const suggestions = this.suggestions;
         let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
         return (isValidData || this.loading) && this.activated;
+      },
+      id() {
+        return `el-autocomplete-${generateId()}`;
       }
     },
     watch: {
@@ -191,14 +209,19 @@
         if (offsetTop < scrollTop) {
           suggestion.scrollTop -= highlightItem.scrollHeight;
         }
-
         this.highlightedIndex = index;
+        this.$el.querySelector('.el-input__inner').setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
       }
     },
     mounted() {
       this.$on('item-click', item => {
         this.select(item);
       });
+      let $input = this.$el.querySelector('.el-input__inner');
+      $input.setAttribute('role', 'textbox');
+      $input.setAttribute('aria-autocomplete', 'list');
+      $input.setAttribute('aria-controls', 'id');
+      $input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
     },
     beforeDestroy() {
       this.$refs.suggestions.$destroy();
diff --git a/packages/button/src/button.vue b/packages/button/src/button.vue
index df454d0cf..b3cd56a3f 100644
--- a/packages/button/src/button.vue
+++ b/packages/button/src/button.vue
@@ -1,6 +1,5 @@
 <template>
   <button
-    v-bind="$props"
     class="el-button"
     @click="handleClick"
     :type="nativeType"
diff --git a/packages/collapse/src/collapse-item.vue b/packages/collapse/src/collapse-item.vue
index a6b8b7172..18ec63e06 100644
--- a/packages/collapse/src/collapse-item.vue
+++ b/packages/collapse/src/collapse-item.vue
@@ -1,11 +1,35 @@
 <template>
   <div class="el-collapse-item" :class="{'is-active': isActive}">
-    <div class="el-collapse-item__header" @click="handleHeaderClick">
-      <i class="el-collapse-item__arrow el-icon-arrow-right"></i>
-      <slot name="title">{{title}}</slot>
+    <div
+      role="tab"
+      :aria-expanded="isActive"
+      :aria-controls="`el-collapse-content-${id}`"
+      :aria-describedby ="`el-collapse-content-${id}`"
+    >
+      <div
+        class="el-collapse-item__header"
+        @click="handleHeaderClick"
+        role="button"
+        :id="`el-collapse-head-${id}`"
+        tabindex="0"
+        @keyup.space.enter.stop="handleEnterClick"
+        :class="{'focusing': focusing}"
+        @focus="focusing = true"
+        @blur="focusing = false"
+      >
+        <i class="el-collapse-item__arrow el-icon-arrow-right"></i>
+        <slot name="title">{{title}}</slot>
+      </div>
     </div>
     <el-collapse-transition>
-      <div class="el-collapse-item__wrap" v-show="isActive">
+      <div
+        class="el-collapse-item__wrap"
+        v-show="isActive"
+        role="tabpanel"
+        :aria-hidden="!isActive"
+        :aria-labelledby="`el-collapse-head-${id}`"
+        :id="`el-collapse-content-${id}`"
+      >
         <div class="el-collapse-item__content">
           <slot></slot>
         </div>
@@ -16,6 +40,7 @@
 <script>
   import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
   import Emitter from 'element-ui/src/mixins/emitter';
+  import { generateId } from 'element-ui/src/utils/util';
 
   export default {
     name: 'ElCollapseItem',
@@ -32,7 +57,8 @@
           height: 'auto',
           display: 'block'
         },
-        contentHeight: 0
+        contentHeight: 0,
+        focusing: false
       };
     },
 
@@ -49,6 +75,9 @@
     computed: {
       isActive() {
         return this.$parent.activeNames.indexOf(this.name) > -1;
+      },
+      id() {
+        return generateId();
       }
     },
 
@@ -60,6 +89,10 @@
     methods: {
       handleHeaderClick() {
         this.dispatch('ElCollapse', 'item-click', this);
+        this.focusing = false;
+      },
+      handleEnterClick() {
+        this.dispatch('ElCollapse', 'item-click', this);
       }
     },
 
diff --git a/packages/collapse/src/collapse.vue b/packages/collapse/src/collapse.vue
index 647c4bfc5..150141300 100644
--- a/packages/collapse/src/collapse.vue
+++ b/packages/collapse/src/collapse.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="el-collapse">
+  <div class="el-collapse" role="tablist" aria-multiselectable="true">
     <slot></slot>
   </div>
 </template>
diff --git a/packages/input-number/src/input-number.vue b/packages/input-number/src/input-number.vue
index cfa3cd697..9794021bf 100644
--- a/packages/input-number/src/input-number.vue
+++ b/packages/input-number/src/input-number.vue
@@ -12,6 +12,8 @@
       class="el-input-number__decrease"
       :class="{'is-disabled': minDisabled}"
       v-repeat-click="decrease"
+      @keydown.enter="decrease"
+      role="button"
     >
       <i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
     </span>
@@ -20,6 +22,8 @@
       class="el-input-number__increase"
       :class="{'is-disabled': maxDisabled}"
       v-repeat-click="increase"
+      @keydown.enter="increase"
+      role="button"
     >
       <i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
     </span>
@@ -36,6 +40,7 @@
       :min="min"
       :name="name"
       ref="input"
+      :label="label"
     >
       <template slot="prepend" v-if="$slots.prepend">
         <slot name="prepend"></slot>
@@ -111,7 +116,8 @@
         type: Number,
         default: 300
       },
-      name: String
+      name: String,
+      label: String
     },
     data() {
       return {
@@ -223,6 +229,18 @@
       this.debounceHandleInput = debounce(this.debounce, value => {
         this.handleInput(value);
       });
+    },
+    mounted() {
+      let innerInput = this.$refs.input.$refs.input;
+      innerInput.setAttribute('role', 'spinbutton');
+      innerInput.setAttribute('aria-valuemax', this.max);
+      innerInput.setAttribute('aria-valuemin', this.min);
+      innerInput.setAttribute('aria-valuenow', this.currentValue);
+      innerInput.setAttribute('aria-disabled', this.disabled);
+    },
+    updated() {
+      let innerInput = this.$refs.input.$refs.input;
+      innerInput.setAttribute('aria-valuenow', this.currentValue);
     }
   };
 </script>
diff --git a/packages/input/src/input.vue b/packages/input/src/input.vue
index be9127ebd..14b50c16e 100644
--- a/packages/input/src/input.vue
+++ b/packages/input/src/input.vue
@@ -13,17 +13,9 @@
   ]">
     <template v-if="type !== 'textarea'">
       <!-- 前置元素 -->
-      <div class="el-input-group__prepend" v-if="$slots.prepend">
+      <div class="el-input-group__prepend" v-if="$slots.prepend"  tabindex="0">
         <slot name="prepend"></slot>
       </div>
-      <!-- 前置内容 -->
-      <span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
-        <slot name="prefix"></slot>
-        <i class="el-input__icon"
-          v-if="prefixIcon"
-          :class="prefixIcon">
-        </i>
-      </span>
       <input
         v-if="type !== 'textarea'"
         class="el-input__inner"
@@ -34,7 +26,16 @@
         @input="handleInput"
         @focus="handleFocus"
         @blur="handleBlur"
+        :aria-label="label"
       >
+      <!-- 前置内容 -->
+      <span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
+        <slot name="prefix"></slot>
+        <i class="el-input__icon"
+           v-if="prefixIcon"
+           :class="prefixIcon">
+        </i>
+      </span>
       <!-- 后置内容 -->
       <span class="el-input__suffix" v-if="$slots.suffix || suffixIcon || validateState">
         <span class="el-input__suffix-inner">
@@ -63,7 +64,9 @@
       v-bind="$props"
       :style="textareaStyle"
       @focus="handleFocus"
-      @blur="handleBlur">
+      @blur="handleBlur"
+      :aria-label="label"
+    >
     </textarea>
   </div>
 </template>
@@ -127,7 +130,8 @@
       },
       onIconClick: Function,
       suffixIcon: String,
-      prefixIcon: String
+      prefixIcon: String,
+      label: String
     },
 
     computed: {
diff --git a/packages/progress/src/progress.vue b/packages/progress/src/progress.vue
index 5dac70687..b1c733e10 100644
--- a/packages/progress/src/progress.vue
+++ b/packages/progress/src/progress.vue
@@ -9,6 +9,10 @@
         'el-progress--text-inside': textInside,
       }
     ]"
+    role="progressbar"
+    :aria-valuenow="percentage"
+    aria-valuemin="0"
+    aria-valuemax="100"
   >
     <div class="el-progress-bar" v-if="type === 'line'">
       <div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}">
diff --git a/packages/rate/src/main.vue b/packages/rate/src/main.vue
index d7158ad32..88c8284b8 100644
--- a/packages/rate/src/main.vue
+++ b/packages/rate/src/main.vue
@@ -1,12 +1,24 @@
 <template>
-  <div class="el-rate">
+  <div class="el-rate"
+       @keydown="handelKey"
+       role="slider"
+       :aria-valuenow="currentValue"
+       :aria-valuetext="text"
+       aria-valuemin="0"
+       :aria-valuemin="max"
+       tabindex="0"
+       @focus="focusing = true"
+       @blur="focusing = false"
+       :class="{'focusing': focusing}"
+  >
     <span
       v-for="item in max"
       class="el-rate__item"
       @mousemove="setCurrentValue(item, $event)"
       @mouseleave="resetCurrentValue"
       @click="selectValue(item)"
-      :style="{ cursor: disabled ? 'auto' : 'pointer' }">
+      :style="{ cursor: disabled ? 'auto' : 'pointer' }"
+    >
       <i
         :class="[classes[item - 1], { 'hover': hoverIndex === item }]"
         class="el-rate__icon"
@@ -34,7 +46,8 @@
         classMap: {},
         pointerAtLeftHalf: true,
         currentValue: this.value,
-        hoverIndex: -1
+        hoverIndex: -1,
+        focusing: false
       };
     },
 
@@ -237,6 +250,34 @@
           this.$emit('input', value);
           this.$emit('change', value);
         }
+        this.focusing = false;
+      },
+
+      handelKey(e) {
+        let currentValue = this.currentValue;
+        const keyCode = e.keyCode;
+        if (keyCode === 38 || keyCode === 39) { // left / down
+          if (this.allowHalf) {
+            currentValue += 0.5;
+          } else {
+            currentValue += 1;
+          }
+          e.stopPropagation();
+          e.preventDefault();
+        } else if (keyCode === 37 || keyCode === 40) {
+          if (this.allowHalf) {
+            currentValue -= 0.5;
+          } else {
+            currentValue -= 1;
+          }
+          e.stopPropagation();
+          e.preventDefault();
+        }
+        currentValue = currentValue < 0 ? 0 : currentValue;
+        currentValue = currentValue > this.max ? this.max : currentValue;
+
+        this.$emit('input', currentValue);
+        this.$emit('change', currentValue);
       },
 
       setCurrentValue(value, event) {
diff --git a/packages/switch/src/component.vue b/packages/switch/src/component.vue
index 5f7af1e84..ce7eaf993 100644
--- a/packages/switch/src/component.vue
+++ b/packages/switch/src/component.vue
@@ -1,5 +1,12 @@
 <template>
-  <label class="el-switch" :class="{ 'is-disabled': disabled, 'is-checked': checked }">
+  <div
+    class="el-switch"
+    :class="{ 'is-disabled': disabled, 'is-checked': checked }"
+    role="switch"
+    :aria-checked="checked"
+    :aria-disabled="disabled"
+    @click="switchValue"
+  >
     <input
       class="el-switch__input"
       type="checkbox"
@@ -8,12 +15,14 @@
       :name="name"
       :true-value="onValue"
       :false-value="offValue"
-      :disabled="disabled">
+      :disabled="disabled"
+      @keydown.enter="switchValue"
+    >
     <span
       :class="['el-switch__label', 'el-switch__label--left', !checked ? 'is-active' : '']"
       v-if="offIconClass || offText">
       <i :class="[offIconClass]" v-if="offIconClass"></i>
-      <span v-if="!offIconClass && offText">{{ offText }}</span>
+      <span v-if="!offIconClass && offText" :aria-hidden="checked">{{ offText }}</span>
     </span>
     <span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }">
       <span class="el-switch__button" :style="{ transform }"></span>
@@ -22,11 +31,10 @@
       :class="['el-switch__label', 'el-switch__label--right', checked ? 'is-active' : '']"
       v-if="onIconClass || onText">
       <i :class="[onIconClass]" v-if="onIconClass"></i>
-      <span v-if="!onIconClass && onText">{{ onText }}</span>
+      <span v-if="!onIconClass && onText" :aria-hidden="!checked">{{ onText }}</span>
     </span>
-  </label>
+  </div>
 </template>
-
 <script>
   export default {
     name: 'ElSwitch',
@@ -114,6 +122,9 @@
         let newColor = this.checked ? this.onColor : this.offColor;
         this.$refs.core.style.borderColor = newColor;
         this.$refs.core.style.backgroundColor = newColor;
+      },
+      switchValue() {
+        this.$refs.input.click();
       }
     },
     mounted() {
diff --git a/packages/theme-chalk/src/collapse.scss b/packages/theme-chalk/src/collapse.scss
index d9a97d44c..4c28cd351 100644
--- a/packages/theme-chalk/src/collapse.scss
+++ b/packages/theme-chalk/src/collapse.scss
@@ -16,6 +16,9 @@
     font-size: $--collapse-header-size;
     font-weight: 500;
     transition: border-bottom-color .3s;
+    &:focus:not(.focusing), &:active {
+      outline-width: 0;
+    }
 
     @include e(arrow) {
       margin-right: 8px;
@@ -42,9 +45,8 @@
   }
 
   @include when(active) {
-    > .el-collapse-item__header {
+    .el-collapse-item__header {
       border-bottom-color: transparent;
-
       .el-collapse-item__arrow {
         transform: rotate(90deg);
       }
diff --git a/packages/theme-chalk/src/rate.scss b/packages/theme-chalk/src/rate.scss
index a9b3f5a2b..a2b30f556 100644
--- a/packages/theme-chalk/src/rate.scss
+++ b/packages/theme-chalk/src/rate.scss
@@ -5,6 +5,10 @@
   height: $--rate-height;
   line-height: 1;
 
+  &:focus:not(.focusing), &:active {
+    outline-width: 0;
+  }
+
   @include e(item) {
     display: inline-block;
     position: relative;
diff --git a/packages/theme-chalk/src/switch.scss b/packages/theme-chalk/src/switch.scss
index 2385cae74..047c17e71 100644
--- a/packages/theme-chalk/src/switch.scss
+++ b/packages/theme-chalk/src/switch.scss
@@ -43,7 +43,13 @@
   }
 
   @include e(input) {
-    display: none;
+    position: absolute;
+    width: 0;
+    height: 0;
+    opacity: 0;
+    &:focus ~ .el-switch__core {
+      outline: 1px solid #f00;
+    }
   }
 
   @include e(core) {
diff --git a/packages/theme-chalk/src/upload.scss b/packages/theme-chalk/src/upload.scss
index 407163311..328f7b6a5 100644
--- a/packages/theme-chalk/src/upload.scss
+++ b/packages/theme-chalk/src/upload.scss
@@ -156,6 +156,17 @@
       }
     }
 
+    & .el-icon-close-tip {
+      display: none;
+      position: absolute;
+      top: 5px;
+      right: 0;
+      cursor: pointer;
+      opacity: 1;
+      color: $--color-primary;
+      transform: translate(15%,0) scale(.7);
+    }
+    
     &:hover {
       background-color: $--background-color-base;
 
@@ -173,12 +184,25 @@
         display: block;
       }
 
-      .el-upload-list__item-name:hover {
+      .el-upload-list__item-name:hover, .el-upload-list__item-name:focus {
         color: $--link-hover-color;
         cursor: pointer;
       }
 
-      &:hover {
+      &:focus {
+        .el-icon-close-tip {
+          display: inline-block;
+        }
+      }
+
+      &:focus:not(.focusing), &:active {
+        outline-width: 0;
+        .el-icon-close-tip {
+          display: none;
+        }
+      }
+
+      &:hover, &:focus {  /*键盘焦点时 显示提示文字 focus*/
         .el-upload-list__item-status-label {
           display: none;
         }
@@ -255,7 +279,6 @@
       .el-icon-close {
         display: none;
       }
-
       &:hover {
         .el-upload-list__item-status-label {
           display: none;
@@ -378,7 +401,6 @@
         .el-upload-list__item-name {
           line-height: 70px;
           margin-top: 0;
-
           i {
             display: none;
           }
diff --git a/packages/upload/src/upload-list.vue b/packages/upload/src/upload-list.vue
index f29ea3b00..763182b44 100644
--- a/packages/upload/src/upload-list.vue
+++ b/packages/upload/src/upload-list.vue
@@ -10,8 +10,13 @@
   >
     <li
       v-for="(file, index) in files"
-      :class="['el-upload-list__item', 'is-' + file.status]"
+      :class="['el-upload-list__item', 'is-' + file.status, focusing ? 'focusing' : '']"
       :key="index"
+      tabindex="0"
+      @keydown.delete="$emit('remove', file)"
+      @focus="focusing = true"
+      @blur="focusing = false"
+      @click="focusing = false"
     >
       <img
         class="el-upload-list__item-thumbnail"
@@ -29,13 +34,14 @@
         }"></i>
       </label>
       <i class="el-icon-close" v-if="!disabled" @click="$emit('remove', file)"></i>
+      <i class="el-icon-close-tip" v-if="!disabled">按delete键可删除</i> <!--因为close按钮只在li:focus的时候 display, li blur后就不存在了,所以键盘导航时永远无法 focus到 close按钮上-->
       <el-progress
         v-if="file.status === 'uploading'"
         :type="listType === 'picture-card' ? 'circle' : 'line'"
         :stroke-width="listType === 'picture-card' ? 6 : 2"
         :percentage="parsePercentage(file.percentage)">
       </el-progress>
-      <span class="el-upload-list__item-actions" v-if="listType === 'picture-card'">
+        <span class="el-upload-list__item-actions" v-if="listType === 'picture-card'">
         <span
           class="el-upload-list__item-preview"
           v-if="handlePreview && listType === 'picture-card'"
@@ -61,6 +67,11 @@
   export default {
     mixins: [Locale],
 
+    data() {
+      return {
+        focusing: false
+      };
+    },
     components: { ElProgress },
 
     props: {
diff --git a/packages/upload/src/upload.vue b/packages/upload/src/upload.vue
index 6e3598ed9..7aca59d30 100644
--- a/packages/upload/src/upload.vue
+++ b/packages/upload/src/upload.vue
@@ -145,6 +145,11 @@ export default {
         this.$refs.input.value = null;
         this.$refs.input.click();
       }
+    },
+    handleKeydown(e) {
+      if (e.keyCode === 13 || e.keyCode === 32) {
+        this.handleClick();
+      }
     }
   },
 
@@ -158,19 +163,21 @@ export default {
       accept,
       listType,
       uploadFiles,
-      disabled
+      disabled,
+      handleKeydown
     } = this;
     const data = {
       class: {
         'el-upload': true
       },
       on: {
-        click: handleClick
+        click: handleClick,
+        keydown: handleKeydown
       }
     };
     data.class[`el-upload--${listType}`] = true;
     return (
-      <div {...data}>
+      <div {...data} tabindex="0" >
         {
           drag
           ? <upload-dragger disabled={disabled} on-file={uploadFiles}>{this.$slots.default}</upload-dragger>
diff --git a/src/utils/util.js b/src/utils/util.js
index f185dcc54..db433b558 100644
--- a/src/utils/util.js
+++ b/src/utils/util.js
@@ -37,3 +37,8 @@ export const getValueByPath = function(object, prop) {
   }
   return result;
 };
+
+export const generateId = function() {
+  return Math.floor(Math.random() * 10000);
+};
+