一个需求温习到的所有知识,h5的表单被键盘遮挡,事件代理,事件委托

牵扯到的两个dom上的方法:

Element.scrollIntoViewIfNeeded()

   Element.scrollIntoViewIfNeeded()方法用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。 
   如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动。
   
   语法:
   element.scrollIntoViewIfNeeded(); // 等同于 element.scrollIntoViewIfNeeded(true)
   element.scrollIntoViewIfNeeded(true);
   element.scrollIntoViewIfNeeded(false);
   参数:
   一个 Boolean 类型的值,默认为true:
   如果为 true,则元素将在其所在滚动区的可视区域中居中对齐。
   如果为 false,则元素将与其所在滚动区的可视区域最近的边缘对齐。 根据可见区域最靠近元素的哪个边缘,
   元素的顶部将与可见区域的顶部边缘对准,或者元素的底部边缘将与可见区域的底部边缘对准。

注意:非标准: 该特性是非标准的,请尽量不要在生产环境中使用它!(2022.07.06)

Element.scrollIntoView()

 Element 接口的 scrollIntoView() 方法会滚动元素的父容器,使被调用 scrollIntoView() 的元素对用户可见。
 
 语法:
 element.scrollIntoView(); // 等同于 element.scrollIntoView(true)
 element.scrollIntoView(alignToTop); // Boolean 型参数
 element.scrollIntoView(scrollIntoViewOptions); // Object 型参数
 参数:
 一个Boolean值:
 1、如果为true,元素的顶端将和其所在滚动区的可视区域的顶端对齐。
 相应的 scrollIntoViewOptions: {block: "start", inline: "nearest"}。这是这个参数的默认值。
 2、如果为false,元素的底端将和其所在滚动区的可视区域的底端对齐。
 相应的scrollIntoViewOptions: {block: "end", inline: "nearest"}。
 一个包含下列属性的对象:
 behavior 可选
 定义动画过渡效果, "auto"或 "smooth" 之一。默认为 "auto"。
 block 可选
 定义垂直方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "start"。
 inline 可选
 定义水平方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "nearest"。
var element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({
    
    block: "end"});
element.scrollIntoView({
    
    behavior: "smooth", block: "end", inline: "nearest"});

注意:取决于其它元素的布局情况,此元素可能不会完全滚动到顶端或底端。

具体实战:

// 我的html结构
 <div class="container" ref="containerForm" v-huiLoading="loading">
    <hm-scroll>
      <hm-validator-group :result="validate">
        <div class="form-item">
          <div class="label-input">
            <div class="name name-require">{
    
    {
    
     $t('vamc_mobile_handle_form_customer_name') }}</div>
            <hm-input :placeholder="$t('vamc_mobile_input_tip')" v-model="userForm.Name"></hm-input>
          </div>
          <hm-validator :model="userForm.Name" :rules="rules.Name" :messages="messages.Name"></hm-validator>
        </div>
        <div class="form-item" style="margin-top: 8.15px">
          <div class="label-input">
            <div class="name name-require">{
    
    {
    
     $t('vamc_mobile_handle_form_street_address') }}</div>
            <hm-input :placeholder="$t('vamc_mobile_input_tip')" v-model="userForm.Street" @blur="getAlternativeAddress"></hm-input>
          </div>
          <hm-validator :model="userForm.Street" :rules="rules.Street" :messages="messages.Street"></hm-validator>
        </div>
        <div class="form-item" style="margin-top: 4px">
          <div class="label-input">
            <div class="name name-require">{
    
    {
    
     $t('vamc_mobile_handle_form_city_town') }}</div>
            <hm-input :placeholder="$t('vamc_mobile_input_tip')" v-model="userForm.City"></hm-input>
          </div>
          <hm-validator :model="userForm.City" :rules="rules.City" :messages="messages.City"></hm-validator>
        </div>
    </hm-scroll>
  </div>
</template>

网上找的方法1:
补充两个知识点:
1、resize方法不管是屏幕的宽度变化或者高度变化都可以检测到,都可以触发这个方法。
2、Document接口的 activeElement 只读属性,用来返回当前在 DOM 或者 shadow DOM 树中处于聚焦状态的Element。

    if ((/Android/gi).test(navigator.userAgent)) {
    
    
      window.addEventListener('resize', function () {
    
    
        if (document.activeElement.tagName == 'INPUT' ||
          document.activeElement.tagName == 'TEXTAREA') {
    
    
          window.setTimeout(function () {
    
    
            document.activeElement.scrollIntoViewIfNeeded();
          }, 0);
        }
      });
    }

网上找的方法2(还是很正确的讲的非常细,具体的链接):

const ua = navigator.userAgent;
const iOS = /iPad|iPhone|iPod/.test(ua);
const input = document.querySelector('#input');
input.addEventListener('focus', () => {
    
    
  setTimeout(() => {
    
    
    if (iOS) {
    
    
      if (!/OS 11_[0-3]\D/.test(ua)) {
    
    
        document.body.scrollTop = document.body.scrollHeight;   
      }
    } else {
    
    
      input.scrollIntoView(false);
    }
  }, 300);
});

我去解决:
i:事件委托来解决
ii:借鉴网上找的方法2进行改造

先从事件委托(事件代理)说起吧

  1. js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上。
  2. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上。
  3. 那就产生了问题:父级那么多子元素,怎么区分事件本应该是哪个子元素的?答案是:event对象里记录的有“事件源”,它就是发生事件的子元素。它存在兼容性问题,在老的IE下,事件源是 window.event.srcElement,其他浏览器是 event.target

用事件委托有什么好处呢?具体的demo例子请参考写的很好,简单明了的阐述了上述的123

第一个好处是效率高,比如,不用for循环为子元素添加事件了
第二个好处是,js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便

具体功能实现代码:

// methods: 只能代理click事件了,因为onfocus代理不到
 handleKeyboardCover() {
    
    
      const ua = navigator.userAgent;
      const iOS = /iPad|iPhone|iPod/.test(ua);
      this.$refs['containerForm'].onclick = function(event) {
    
    
        let dom = event.srcElement || event.target;
        if (dom.nodeName.toLowerCase() === 'input' || dom.nodeName.toLowerCase() === 'textarea') {
    
    
          console.log('出发了获取点击事件', event, event.target);
          setTimeout(() => {
    
    
            if (iOS) {
    
    
              if (!/OS 11_[0-3]\D/.test(ua)) {
    
    
              // document.body.scrollTop = document.body.scrollHeight; // 当ios上出现键盘遮挡的时进行处理
              }
            } else {
    
    
              event.target.scrollIntoView({
    
    behavior: "smooth", block: "center", inline: "nearest"});
            }
          }, 300);
        }
      };
    }
// 生命周期 
 mounted() {
    
    
    this.handleCreateMap();
    this.handleKeyboardCover();
  }

猜你喜欢

转载自blog.csdn.net/weixin_43131046/article/details/125643282