Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call function destroyed callback in last call

Tags:

javascript

I have function, which create popup similar as alert, confirm or prompt. Problem occurs when is called new popup (any type), then is destroyed callback in first call (prompt value)

PoppyPopup.prompt('Type your name', 'User control', {}, function(value) {
    PoppyPopup.alert('Welcome ' + value); //<-- value is id of second popup
});

PoppyPopup.alert('Hi, this is a PopUp!', 'With title!');

DEMO: https://jsfiddle.net/yojz8sb3/

Edit: When is call one time, it's OK, I really dont know where is problem.

like image 502
alexso Avatar asked Mar 24 '26 21:03

alexso


1 Answers

Well, the short answer is because PoppyPopup isn't designed to handle having more than one popup open at a time.

popupType is shared between all popups so when you open the alert popup it sets popupType to ALERT and so when the prompt popup's accept callback is called it's handled as if it's an ALERT type popup.


One (fairly hacky) solution is to remove var popupType = null; and instead pass popupType to the Popup constructor as a 4th argument. Then you can change

PoppyPopup.accept(basePopup.id, options);

to

PoppyPopup.accept(basePopup.id, options, popupType);

in Popup function.

And change

accept: function(popupId, options) {

to

accept: function(popupId, options, popupType) {

that way the popup type is associated with the popup instance instead of being shared among all popups.

Here is a working example: https://jsfiddle.net/0xpz7f9L/


However my answer above doesn't fix the issue that clicking the accept button appears to close a random popup (obviously not ideal). I'd recommend rewriting the whole thing to simplify it and make it easier to read/understand. Also this sort of thing traditionally uses promises instead of callbacks.

Here is how I would rewrite it:

const PoppyPopup = (function() {
  let nextPopupId = 0;

  return {
    alert: function(message, title, options) {
      const realOptions = buildOptionsFrom(options);
      const popupElem = buildPopupElem(title, message, realOptions, false, false);

      const promise = new Promise(resolve => {
        popupElem.querySelector('.accept').onclick = resolve;
      });
      promise.finally(() => close(popupElem, realOptions.removeWhenClosed));
      document.querySelector('body').appendChild(popupElem);

      return promise;
    },

    confirm: function(message, title, options) {
      const realOptions = buildOptionsFrom(options);
      const popupElem = buildPopupElem(title, message, realOptions, false, true);

      const promise = new Promise((resolve, reject) => {
        popupElem.querySelector('.accept').onclick = resolve;
        popupElem.querySelector('.cancel').onclick = reject;
      });
      promise.finally(() => close(popupElem, realOptions.removeWhenClosed));
      document.querySelector('body').appendChild(popupElem);

      return promise;
    },

    prompt: function(message, title, options) {
      const realOptions = buildOptionsFrom(options);
      const popupElem = buildPopupElem(title, message, realOptions, true, true);

      const promise = new Promise((resolve, reject) => {
        popupElem.querySelector('.accept').onclick = () => resolve(popupElem.querySelector('input').value);
        popupElem.querySelector('.cancel').onclick = reject;
      });
      promise.finally(() => close(popupElem, realOptions.removeWhenClosed));
      document.querySelector('body').appendChild(popupElem);

      return promise;
    }
  };
  
  function close(popupElem, removeWhenClosed) {
    popupElem.classList.remove('show');

    popupElem.addEventListener('transitionend', function(e) {
      if (e.propertyName === 'opacity' && removeWhenClosed) {
        popupElem.parentNode.removeChild(popupElem);
      }
    });
  }
  
  function buildOptionsFrom(options) {
    return Object.assign({
      showBackground: true,
      removeWhenClosed: true,
      width: '400px'
    }, options || {});
  }

  function buildPopupElem(title, message, options, provideInput, provideCancel) {
    const basePopupDiv = document.createElement("DIV");
    basePopupDiv.className = "poppy-popup show";
    basePopupDiv.id = nextPopupId++;
    
    if (options.showBackground) { 
      const backgroundDiv = document.createElement("DIV");
      backgroundDiv.className = "poppy-popup-background";
      basePopupDiv.appendChild(backgroundDiv);
    }
    
    const containerDiv = document.createElement("DIV");
    containerDiv.className = "poppy-popup-container";
    containerDiv.style.width = options.width;
    containerDiv.style.height = options.height;
    
    if (title) {
      const headerTitleDiv = document.createElement("DIV");
      headerTitleDiv.className = "poppy-popup-title-text";
      headerTitleDiv.innerHTML = title;
    
      const headerDiv = document.createElement("DIV");
      headerDiv.className = "poppy-popup-header";
      headerDiv.appendChild(headerTitleDiv);

      containerDiv.appendChild(headerDiv);
    }
    
    const contentDiv = document.createElement("DIV");
    contentDiv.className = "poppy-popup-content";
    contentDiv.innerHTML = message;
    
    if (provideInput) {
      const input = document.createElement("INPUT");
      input.type = "text";
      contentDiv.appendChild(input);
    }
    
    const acceptLink = document.createElement("A");
    acceptLink.className = 'accept';
    acceptLink.href = "#";
    acceptLink.innerHTML = "<i class='material-icons'>done</i> OK";
    
    const acceptSpan = document.createElement("SPAN");
    acceptSpan.className = "poppy-popup-accept";
    acceptSpan.appendChild(acceptLink);
    
    const buttonsDiv = document.createElement("DIV");
    buttonsDiv.className = "poppy-popup-buttons";
    buttonsDiv.appendChild(acceptSpan);
    
    if (provideCancel) {
      const cancelLink = document.createElement("A");
      cancelLink.className = "cancel";
      cancelLink.href = "#";
      cancelLink.innerHTML = "<i class='material-icons'>close</i> Cancel";
    
      const cancelSpan = document.createElement("SPAN");
      cancelSpan.className = "poppy-popup-cancel";
      cancelSpan.appendChild(cancelLink);
      
      buttonsDiv.appendChild(cancelSpan);
    }
    
    containerDiv.appendChild(contentDiv);
    containerDiv.appendChild(buttonsDiv);

    basePopupDiv.appendChild(containerDiv);

    return basePopupDiv;
  } //end of buildPopupElem()
})();

PoppyPopup.prompt('Type your name', 'User control').then(value => {
  PoppyPopup.alert('Welcome ' + value).then(() => {
    PoppyPopup.confirm('And just for completeness, confirm?')
      .then(() => alert('accepted'), () => alert('rejected'));
  });
});

PoppyPopup.alert('Hi, this is a PopUp!', 'With title!');
*, *:before, *:after {
  box-sizing: border-box; }

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
  background-color: #eeeeee; }

.poppy-popup {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  width: 100vw;
  height: 100vh;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-pack: center;
      justify-content: center;
  -ms-flex-align: center;
      align-items: center;
  font-size: 16px;
  opacity: 0;
  transition: opacity .3s; }
  .poppy-popup.show {
    opacity: 1; }
  .poppy-popup > .poppy-popup-background {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1010;
    background-color: rgba(0, 0, 0, 0.5);
    width: 100%;
    height: 100%; }
  .poppy-popup > .poppy-popup-container {
    max-width: 90%;
    max-height: 90%;
    width: 100%;
    position: relative;
    z-index: 1020;
    border-radius: 3px;
    box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3); }
    .poppy-popup > .poppy-popup-container > .poppy-popup-header {
      background-color: #FFFFFF;
      color: #222222;
      height: 50px;
      width: 100%;
      position: relative;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px; }
      .poppy-popup > .poppy-popup-container > .poppy-popup-header > .poppy-popup-title-text {
        width: 100%;
        max-height: 50px;
        font-size: 1.5em;
        text-align: center;
        line-height: 50px;
        text-overflow: ellipsis;
        font-weight: bold;
        overflow: hidden;
        white-space: nowrap; }
    .poppy-popup > .poppy-popup-container > .poppy-popup-content {
      background-color: #FFFFFF;
      width: 100%;
      padding: 10px 20px;
      border-left: 1px solid #DDDDDD;
      border-right: 1px solid #DDDDDD;
      overflow: auto; }
      .poppy-popup > .poppy-popup-container > .poppy-popup-content > input[type="text"] {
        background-color: transparent;
        border-width: 0;
        border-bottom: 2px solid #CCCCCC;
        outline: none;
        font-size: 1.3em;
        width: 100%;
        margin-top: 20px;
        padding: 5px;
        box-shadow: none;
        transition: border-bottom .2s; }
        .poppy-popup > .poppy-popup-container > .poppy-popup-content > input[type="text"]:focus {
          border-bottom: 2px solid #2088AD; }
    .poppy-popup > .poppy-popup-container > .poppy-popup-buttons {
      width: 100%;
      height: 50px;
      padding: 0 10px;
      background-color: #FFFFFF;
      border-bottom-left-radius: 3px;
      border-bottom-right-radius: 3px;
      overflow: hidden; }
      .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept, .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel {
        width: 120px;
        display: block;
        float: right;
        color: #2088AD; }
        .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept > a, .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel > a {
          display: block;
          color: inherit;
          text-decoration: none;
          text-align: center;
          padding: 10px 0;
          border-radius: 3px;
          transition: background-color .2s, box-shadow .1s; }
          .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept > a:active, .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel > a:active {
            box-shadow: inset 0 0 5px 1px rgba(0, 0, 0, 0.3); }
          .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept > a > i, .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel > a > i {
            vertical-align: middle;
            margin-top: -3px; }
      .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept {
        right: 0;
        color: #2088AD; }
        .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-accept > a:hover {
          background-color: rgba(0, 0, 0, 0.1); }
      .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel {
        left: 0;
        color: #2088AD; }
        .poppy-popup > .poppy-popup-container > .poppy-popup-buttons > .poppy-popup-cancel > a:hover {
          background-color: rgba(0, 0, 0, 0.1); }

Note: The css is unchanged.

like image 57
Rocky Sims Avatar answered Mar 27 '26 12:03

Rocky Sims