Materialize: Clock displays and hides immediately after updating Chrome version to 73

Created on 22 Mar 2019  路  34Comments  路  Source: Dogfalo/materialize

Hi,

When we click on the filed the clock modal opens and immediately close
https://codepen.io/anon/pen/evQxPy
This happens after I have updated my chrome version to 73

Most helpful comment

To fix the date picker change the code from:

// Only bind keydown events if the element isn鈥檛 editable.
if (!SETTINGS.editable) {

    $ELEMENT.

        // On focus/click, focus onto the root to open it up.
        on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
            event.preventDefault();
            P.$root.eq(0).focus();
        }).

        // Handle keyboard event based on the picker being opened or not.
        on('keydown.' + STATE.id, handleKeydownEvent);
}

to:

// Only bind keydown events if the element isn鈥檛 editable.
if (!SETTINGS.editable) {

    $ELEMENT.

        // On focus/click, focus onto the root to open it up.
        on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
            setTimeout(function(){
                event.preventDefault();
                P.$root.eq(0).focus();
            }, 100);
        }).

        // Handle keyboard event based on the picker being opened or not.
        on('keydown.' + STATE.id, handleKeydownEvent);
}

All 34 comments

Any quick fix on this?

How to fix this error?

The same here!! :( This problem happens only in Chrome, Edge works fine.

It麓s a race condition

https://bugs.chromium.org/p/chromium/issues/detail?id=941910

The workaround is a setTimeout on root focus.

Exactly. Either throttle / debounce (see pickadate 3.6.1) or use the setTimeout workaround.

@Marzon We use materialize.js and where to use setTimeout?

I am also looking for workaround for time picker cause upgrading to new materialcss is time consuming. Please let us know workaround for time picker

For datepicker
comment this code in materialize.js file
P.close(target === P.$root.children()[0]);

It麓s a race condition

https://bugs.chromium.org/p/chromium/issues/detail?id=941910

The workaround is a setTimeout on root focus.

Can please specify where to add setTimeout in the JS file. That would be really helpful

See https://github.com/amsul/pickadate.js/pull/1140 with the right workaround.

The other proposed change breaks focus (a11y).

@DanielRuf it's quite hard to find the places to change the logic based on github.com/amsul/pickadate.js/pull/1140/files these changes, on materialize.js file.
It would much be appreciated any solution on materialize.js

Hi! On materialize.js, add a setTimeout (100 ms) in end of the

  open: function (dontGiveFocus) {

Move de $document.on('click.' . .... and the

P.$root.eq(0).focus();

to the setTimeOut function.

Leave document.on first and $root.focus() after document.on ..

I麓m waiting the race condition bug to be fixed.. before to propose a PR..

Hi,
Not sure, if this is a bulletproof solution but this works for me especially with clockpicker.
The version should be materializecss (v0.100.2). In materialize.js file
Line No: 8913

ClockPicker.prototype.show = function (e) {

Just wrap setTimeout( 200 ms) inside this whole function, works well.

Hi,
Not sure, if this is a bulletproof solution but this works for me especially with clockpicker.
The version should be materializecss (v0.100.2). In materialize.js file
Line No: 8913

ClockPicker.prototype.show = function (e) {

Just wrap setTimeout( 100 ms) inside this whole function, works well.

Now it works fine
`

ClockPicker.prototype.show = function (e) {
var _this = this;

setTimeout(function () {
// Not show again
if (_this.isShown) {
return;
}

raiseCallback(_this.options.beforeShow);
$(':input').each(function () {
  $(this).attr('tabindex', -1);
});
var self = _this; // Initialize

_this.input.blur();

_this.popover.addClass('picker--opened');

_this.input.addClass('picker__input picker__input--active');

$(document.body).css('overflow', 'hidden'); // Get the time

var value = ((_this.input.prop('value') || _this.options['default'] || '') + '').split(':');

if (_this.options.twelvehour && !(typeof value[1] === 'undefined')) {
  if (value[1].indexOf("AM") > 0) {
    _this.amOrPm = 'AM';
  } else {
    _this.amOrPm = 'PM';
  }

  value[1] = value[1].replace("AM", "").replace("PM", "");
}

if (value[0] === 'now') {
  var now = new Date(+new Date() + _this.options.fromnow);
  value = [now.getHours(), now.getMinutes()];

  if (_this.options.twelvehour) {
    _this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
  }
}

_this.hours = +value[0] || 0;
_this.minutes = +value[1] || 0;

_this.spanHours.html(_this.hours);

_this.spanMinutes.html(leadingZero(_this.minutes));

if (!_this.isAppended) {
  // Append popover to input by default
  var containerEl = document.querySelector(_this.options.container);

  if (_this.options.container && containerEl) {
    containerEl.appendChild(_this.popover[0]);
  } else {
    _this.popover.insertAfter(_this.input);
  }

  if (_this.options.twelvehour) {
    if (_this.amOrPm === 'PM') {
      _this.spanAmPm.children('#click-pm').addClass("text-primary");

      _this.spanAmPm.children('#click-am').removeClass("text-primary");
    } else {
      _this.spanAmPm.children('#click-am').addClass("text-primary");

      _this.spanAmPm.children('#click-pm').removeClass("text-primary");
    }
  } // Reset position when resize


  $win.on('resize.clockpicker' + _this.id, function () {
    if (self.isShown) {
      self.locate();
    }
  });
  _this.isAppended = true;
} // Toggle to hours view


_this.toggleView('hours'); // Set position


_this.locate();

_this.isShown = true; // Hide when clicking or tabbing on any element except the clock and input

$doc.on('click.clockpicker.' + _this.id + ' focusin.clockpicker.' + _this.id, function (e) {
  var target = $(e.target);

  if (target.closest(self.popover.find('.picker__wrap')).length === 0 && target.closest(self.input).length === 0) {
    self.hide();
  }
}); // Hide when ESC is pressed

$doc.on('keyup.clockpicker.' + _this.id, function (e) {
  if (e.keyCode === 27) {
    self.hide();
  }
});
raiseCallback(_this.options.afterShow);

}, 200);
};

`

@DavinderPRO This is what I mean, does this work for you?

// Show popover
  ClockPicker.prototype.show = function (e) {
    setTimeout(() => {
      // Not show again
      if (this.isShown) {
        return;
      }
      raiseCallback(this.options.beforeShow);
      $(':input').each(function () {
        $(this).attr('tabindex', -1);
      });

      var self = this;
      // Initialize
      this.input.blur();
      this.popover.addClass('picker--opened');
      this.input.addClass('picker__input picker__input--active');
      $(document.body).css('overflow', 'hidden');
      // Get the time
      var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
      if (this.options.twelvehour && !(typeof value[1] === 'undefined')) {
        if (value[1].indexOf("AM") > 0) {
          this.amOrPm = 'AM';
        } else {
          this.amOrPm = 'PM';
        }
        value[1] = value[1].replace("AM", "").replace("PM", "");
      }
      if (value[0] === 'now') {
        var now = new Date(+new Date() + this.options.fromnow);
        value = [now.getHours(), now.getMinutes()];
        if (this.options.twelvehour) {
          this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
        }
      }
      this.hours = +value[0] || 0;
      this.minutes = +value[1] || 0;
      this.spanHours.html(this.hours);
      this.spanMinutes.html(leadingZero(this.minutes));
      if (!this.isAppended) {

        // Append popover to input by default
        var containerEl = document.querySelector(this.options.container);
        if (this.options.container && containerEl) {
          containerEl.appendChild(this.popover[0]);
        } else {
          this.popover.insertAfter(this.input);
        }

        if (this.options.twelvehour) {
          if (this.amOrPm === 'PM') {
            this.spanAmPm.children('#click-pm').addClass("text-primary");
            this.spanAmPm.children('#click-am').removeClass("text-primary");
          } else {
            this.spanAmPm.children('#click-am').addClass("text-primary");
            this.spanAmPm.children('#click-pm').removeClass("text-primary");
          }
        }
        // Reset position when resize
        $win.on('resize.clockpicker' + this.id, function () {
          if (self.isShown) {
            self.locate();
          }
        });
        this.isAppended = true;
      }
      // Toggle to hours view
      this.toggleView('hours');
      // Set position
      this.locate();
      this.isShown = true;
      // Hide when clicking or tabbing on any element except the clock and input
      $doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function (e) {
        var target = $(e.target);
        if (target.closest(self.popover.find('.picker__wrap')).length === 0 && target.closest(self.input).length === 0) {
          self.hide();
        }
      });
      // Hide when ESC is pressed
      $doc.on('keyup.clockpicker.' + this.id, function (e) {
        if (e.keyCode === 27) {
          self.hide();
        }
      });
      raiseCallback(this.options.afterShow);
    }, 200);
  };

To fix the date picker change the code from:

// Only bind keydown events if the element isn鈥檛 editable.
if (!SETTINGS.editable) {

    $ELEMENT.

        // On focus/click, focus onto the root to open it up.
        on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
            event.preventDefault();
            P.$root.eq(0).focus();
        }).

        // Handle keyboard event based on the picker being opened or not.
        on('keydown.' + STATE.id, handleKeydownEvent);
}

to:

// Only bind keydown events if the element isn鈥檛 editable.
if (!SETTINGS.editable) {

    $ELEMENT.

        // On focus/click, focus onto the root to open it up.
        on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
            setTimeout(function(){
                event.preventDefault();
                P.$root.eq(0).focus();
            }, 100);
        }).

        // Handle keyboard event based on the picker being opened or not.
        on('keydown.' + STATE.id, handleKeydownEvent);
}

+1

+1

@markonose Are you sure that event.preventDefault(); works as intended in a setTimout()?
I suspect you only want the .focus() call delayed.

@ray007 You are indeed correct, but at least in my case the event.preventDefault(); does nothing.

// On focus/click, focus onto the root to open it up.
on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
    event.preventDefault();

    setTimeout(function(){
        P.$root.eq(0).focus();
    }, 100);
}).

would work more like the original code intended

If anyone stumbles uppon this thread here's how to fix the problem for the select dropdown

$newSelect.on({
    'focus': function () {
        var _this = this;

        setTimeout(function () {
            if ($('ul.select-dropdown').not(options[0]).is(':visible')) {
                $('input.select-dropdown').trigger('close');
                $(window).off('click.select');
            }
            if (!options.is(':visible')) {
                $(_this).trigger('open', ['focus']);
                var label = $(_this).val();
                if (multiple && label.indexOf(',') >= 0) {
                    label = label.split(',')[0];
                }

                var selectedOption = options.find('li').filter(function () {
                    return $(_this).text().toLowerCase() === label.toLowerCase();
                })[0];
                activateOption(options, selectedOption, true);

                $(window).off('click.select').on('click.select', function () {
                    multiple && (optionsHover || $newSelect.trigger('close'));
                    $(window).off('click.select');
                });
            }
        }, 75);
    },
    'click': function (e) {
        e.stopPropagation();
    }
});

1.0.0 does not have this problem so if you are able to upgrade, I would recommend that route. However if you can't, try one of the fixes in this thread. Potentially we may add a fix for this if chrome doesn't revert this behavior.

@Dogfalo do you know what was the change introduced in Chrome that produces this behaviour?

I think it has to do with the timing of events when clicking an input. Where before it was click -> focus, it seems to now be focus -> click. This is just a guess as I haven't looked deeply into the issue yet.

If anyone stumbles uppon this thread here's how to fix the problem for the select dropdown

$newSelect.on({
  'focus': function () {
      var _this = this;

      setTimeout(function () {
          if ($('ul.select-dropdown').not(options[0]).is(':visible')) {
              $('input.select-dropdown').trigger('close');
              $(window).off('click.select');
          }
          if (!options.is(':visible')) {
              $(_this).trigger('open', ['focus']);
              var label = $(_this).val();
              if (multiple && label.indexOf(',') >= 0) {
                  label = label.split(',')[0];
              }

              var selectedOption = options.find('li').filter(function () {
                  return $(_this).text().toLowerCase() === label.toLowerCase();
              })[0];
              activateOption(options, selectedOption, true);

              $(window).off('click.select').on('click.select', function () {
                  multiple && (optionsHover || $newSelect.trigger('close'));
                  $(window).off('click.select');
              });
          }
      }, 75);
  },
  'click': function (e) {
      e.stopPropagation();
  }
});

Above fix was not showing the already selected item from drop-down hence i modified it little bit. below is changed code.

$newSelect.on({
'focus': function () {

                    if ($('ul.select-dropdown').not(options[0]).is(':visible')) {
                        $('input.select-dropdown').trigger('close');
                    }
                    if (!options.is(':visible')) {       

                        setTimeout(function () {
                            $(this).trigger('open', ['focus']);
                        }, 70);

                        var label = $(this).val();
                        var selectedOption = options.find('li').filter(function () {
                            return $(this).text().toLowerCase() === label.toLowerCase();
                        })[0];
                        activateOption(options, selectedOption);
                    }

            },
            'click': function (e) {
                e.stopPropagation();
            },

@Dogfalo Version 74 of Chrome is released and they didn't fix this, reading the comment 23 in https://bugs.chromium.org/p/chromium/issues/detail?id=941910#c6 it doesn't seem they are going to fix it, couldn't you please just fix it in materializecss?

@Dogfalo Version 74 of Chrome is released and they didn't fix this, reading the comment 23 in bugs.chromium.org/p/chromium/issues/detail?id=941910#c6 it doesn't seem they are going to fix it, couldn't you please just fix it in materializecss?

Well it will not fix old releases as 0.x is not actively developed anymore but 1.x.
Also this is opensource so anyone can provide a PR to fix this.
We had to do another workaround as the timeout / debouncing did not completely fix this bug in Chrome.

I thought this may help some who stumbled on this page. I am using angular2-materialize and my materialize select input sometimes closes immediately after opening.

I solved this by adding a click listener on a div that wraps the select tag, with nothing but event.stopPropagation() in the listener. This fixed the issue for me.

materialize.js

For anyone who is too lazy to edit the file yourself

@LemuelHui

Awesome =)

Is there also a minified patched version? If you need help with that, just let me know.

PS: the timeout does not really fix that (longpress click).

You can check how we have fixed it in pickadate.js.

@DanielRuf could you provide a link to the commit with the fix that you did for pickadate?

@Sebastriani see https://github.com/amsul/pickadate.js/commit/31789ebf7fb9c832f0e8e06003654e20ab91c63d

My initial approach using timers (with setTimeout) did not work in all but many cases as it was added at the top of the counter stack of the VM.

The quick fix in the original codepen example (https://codepen.io/anon/pen/evQxPy) in this issue would be a code snippet like this:
document.getElementById('birthdate').addEventListener('pointerdown', (e)=>{e.target.setPointerCapture(e.pointerId);})
I was going to send a pull request to fix this issue, but I'm having some trouble with this repo and understanding how I should get a local example with un-built files to work. I can see from the documentations that we should not modify materialize.js directly. But I'm not able to produce a local example similar to the codepen example that includes the unbuilt files to verify the fix locally before sending the pull request. Can someone guide me here on what files from js/ directory I should include + the html/css in the code pen to reproduce the issue locally? Alternatively if you could just attach a html file that include the local files from this repo and reproduce the issue and I can work on the fix.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

heshamelmasry77 picture heshamelmasry77  路  3Comments

ReiiYuki picture ReiiYuki  路  3Comments

PhillippOhlandt picture PhillippOhlandt  路  3Comments

SoproniOli713 picture SoproniOli713  路  3Comments

serkandurusoy picture serkandurusoy  路  3Comments