restrict addEventDelegate to parent hbox only

我的未来我决定 提交于 2020-06-29 04:21:09

问题


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

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>');
    }
  });
});

Here is the video of the issue https://streamable.com/fjw408


回答1:


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.

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()
}

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,output




回答2:


i think you can do without the hbox; by just having two formatted text or any sapui5 controls will be ok too.

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')
    },

    onBeforeRendering: function () {
      const highlighedCurrency = this.getCurrency().repeat(this.getRank());
      const highlight = new FormattedText({
        htmlText: highlighedCurrency
      });
      this.setAggregation('_highlighted', highlight);

      const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
      const faded = new FormattedText({
        htmlText: fadedCurrency
      });
      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);
    },

    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>');
    },
    onAfterRendering: function () {
      const popOver = this.getAggregation('_popover');
      const highlighted = this.getAggregation("_highlighted");
      highlighted.$().addClass("currency-highlighted");
      highlighted.$().hover(function() {
        popOver.openBy(highlighted);
      }, function() {
        popOver.close();
      });
      const faded = this.getAggregation("_faded");
      faded.$().addClass("currency-faded");
    },
  });
  (new TherapyCosts({
    rank: 5
  })).placeAt('content')
});

https://jsbin.com/razebuq/edit?css,js,output



来源:https://stackoverflow.com/questions/62413287/restrict-addeventdelegate-to-parent-hbox-only

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!