仅将 addEventDelegate 限制为父 hbox [英] restrict addEventDelegate to parent hbox only

查看:41
本文介绍了仅将 addEventDelegate 限制为父 hbox的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是为我之前的问题创建的自定义控件货币符号的自定义字体

This is a custom control created for my previous question Custom font for currency signs

我有两个 span 元素彼此相邻.它们位于 FormattedText 中.FormattedText 本身位于 HBox 中.我希望当用户将鼠标悬停在 hbox 上时触发弹出窗口.不幸的是,由于我有 2 个跨度,因此当用户将鼠标悬停在它们上方时会分别触发(因此显示 2 个单独的弹出框,而实际上它应该是一个).我的假设是这是因为 onmouseover/out 附加到引擎盖下的两个跨度.我可以将这些事件限制为仅 hbox 吗?

I have two span elements coming next to each other. They sit in the FormattedText. The FormattedText itself sits in the HBox. I want popover fired when user mouses over/out from the hbox. Unfortunately, as I have 2 spans this fires separately when user hovers overs them (thus showing 2 separate popovers, when in fact it should be one). My assumption is that this causes because onmouseover/out is attached to both spans under the hood. Can I restrict these events to hbox only?

sap.ui.define([
  'sap/ui/core/Control',
  'sap/m/FormattedText',
  'sap/m/HBox',
], function (Control, FormattedText, HBox) {

  return Control.extend('drex.control.TherapyCosts', {
    metadata: {
      properties: {
        rank: {
          type: 'int',
          defaultValue: 0
        },
      },
      aggregations: {
        _rankedTherapyCost: {type: 'sap.m.FormattedText', multiple: false, singularName: '_rankedTherapyCost'},
        _popover: {type: 'sap.m.Popover', multiple: false, singularName: '_popover'},
        _hbox: {type: 'sap.m.HBox', multiple: false}
      }
    },

    init: function () {
      Control.prototype.init.apply(this, arguments);
    },

    onBeforeRendering: function () {
      const highlighedCurrency = this.getCurrency().repeat(this.getRank());
      const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
      const _popover = new sap.m.Popover({
        showHeader: false,
        placement: 'VerticalPreferredBottom',
        content: new sap.m.Text({text: 'test'})
      });
      this.setAggregation('_popover', _popover);
      const _formattedText = new FormattedText({
        htmlText:
          `<span class="currencyHighlighted">${highlighedCurrency}</span>` +
          `<span class="currencyFaded">${fadedCurrency}</span>`
      });
      this.setAggregation('_rankedTherapyCost', _formattedText);
      const _hbox = new HBox(
        { items: [this.getAggregation('_rankedTherapyCost')]})
        .addEventDelegate({
          onmouseover: () => {
            this.getAggregation('_popover').openBy(this);
          },
          onmouseout: () => {
            this.getAggregation('_popover').close()
          }
        });
      this.setAggregation('_hbox', _hbox)
    },

    renderer: function (rm, oControl) {
      const _hbox = oControl.getAggregation('_hbox');
      rm.write('<div');
      rm.writeControlData(oControl);
      rm.write('>');
      rm.renderControl(_hbox);
      rm.write('</div>');
    }
  });
});

这是问题的视频https://streamable.com/fjw408

推荐答案

这里的关键是当鼠标移出正在侦听事件的元素的任何子元素时触发 mouseout 事件.在这种情况下,例如,从强调文本移动到淡化文本,当移出强调文本时,您会收到一个 mouseout 事件,关闭弹出窗口,然后在淡化文本上的鼠标悬停事件再次打开它.

The key here is the mouseout event is triggered when the mouse moves out of any child element of the element that is listening for the event. In this case, for example, moving from the emphasised text to the faded text, you get a mouseout event when moving off the emphasised text, which closes the popup, then a mouseover event on the faded text which opens it again.

因为您打开一个已经打开的弹出窗口并没有做任何事情,所以您只需要在 mouseout 上添加一行来检查您所访问的元素.如果它不是当前元素的子元素,则仅关闭弹出框.

Since you opening an already open popup doesn't do anything, you only need add a line on mouseout to inspect the element that you've gone to. If it is not a child of the current element, only then close the popover.

if (!this.getDomRef().contains(e.toElement)) {
  popOver.close()
}

根据 D.Seah 的回答,我已将其添加到 JSBin 中.我个人不喜欢像这样使用 onBeforeRenderingonAfterRendering,所以我重构了一点以在 init 上构造所有内容,并简单地更改属性更改时的控件.这样一来,出于性能原因,您在渲染方面的工作量就会减少.

Building on D.Seah's answer, I've added this to the JSBin. I personally don't like using onBeforeRendering and onAfterRendering like this, so I've refactored a little to construct everything on init and simply change the controls on property change. This way, you're doing less on rendering for performance reasons.

sap.ui.define([
  'sap/ui/core/Control',
  'sap/m/FormattedText',
], function (Control, FormattedText) {

  Control.extend('TherapyCosts', {
    metadata: {
      properties: {
        rank: {
          type: 'int',
          defaultValue: 0
        },
        currency: {
          type: "string",
          defaultValue: "$"
        }
      },
      aggregations: {
        _highlighted: {type: 'sap.m.FormattedText', multiple: false},
        _faded: {type: 'sap.m.FormattedText', multiple: false},
        _popover: {type: 'sap.m.Popover', multiple: false, singularName: '_popover'}
      }
    },

    init: function () {
      Control.prototype.init.apply(this, arguments);
      this.addStyleClass('therapy-cost');
      
      const highlight = new FormattedText();
      highlight.addStyleClass("currency-highlight");
      this.setAggregation('_highlighted', highlight);

      const faded = new FormattedText();
      faded.addStyleClass("currency-faded");
      this.setAggregation('_faded', faded);
      
      const _popover = new sap.m.Popover({
        showHeader: false,
        placement: 'VerticalPreferredBottom',
        content: new sap.m.Text({text: 'test'})
      });
      this.setAggregation('_popover', _popover);
    },
    
    _changeAggr: function () {
      const highlighedCurrency = this.getCurrency().repeat(this.getRank());
      const highlight = this.getAggregation("_highlighted");
      highlight.setHtmlText(highlighedCurrency);
      
      const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
      const faded = this.getAggregation("_faded");
      faded.setHtmlText(fadedCurrency);
    },
    
    setRank: function(rank) {
       if (this.getProperty("rank") !== rank) {
         this.setProperty("rank", rank);
         this._changeAggr();
       }
    },

    setCurrency: function(curr) {
       if (this.getProperty("currency") !== curr) {
         this.setProperty("currency", curr);
         this._changeAggr();
       }
    },
    
    renderer: function (rm, oControl) {
      rm.write('<div');
      rm.writeControlData(oControl);
      rm.writeClasses(oControl);
      rm.writeStyles(oControl);
      rm.write('>');
      rm.renderControl(oControl.getAggregation('_highlighted'));
      rm.renderControl(oControl.getAggregation('_faded'));
      rm.write('</div>');
    },
    
    onmouseover: function (e) {
      const popOver = this.getAggregation('_popover');
      popOver.openBy(this);
    },
    
    onmouseout: function (e) {
      if (!this.getDomRef().contains(e.toElement)) {
        const popOver = this.getAggregation('_popover');
        popOver.close();
      }
    }
  });
  (new TherapyCosts({
    rank: 5,
    currency: "£"
  })).placeAt('content')
});

.therapy-cost {
  display: inline-flex;
  flex-direction: row;
}

.therapy-cost .currency-highlighted {
  color: black;
}

.therapy-cost .currency-faded {
  color: lightgrey;
}

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script 
            src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" 
            id="sap-ui-bootstrap" 
            data-sap-ui-theme="sap_bluecrystal" 
            data-sap-ui-xx-bindingSyntax="complex" 
            data-sap-ui-libs="sap.m"></script>
  </head>
  <body class="sapUiBody sapUiSizeCompact">
    <div id='content'></div>
  </body>
</html>

https://jsbin.com/gukedamemu/3/edit?css,js,输出

这篇关于仅将 addEventDelegate 限制为父 hbox的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆