Problem/Motivation
Clicking a contextual links trigger (the pencil button) sometimes does not open the menu. A second click is needed. This happens in Chromium-based browsers when focus is somewhere else when you click, for example on a link inside the same contextual region, or on another contextual trigger.
In Chromium, a mouse click on a <button> moves focus to that button on mousedown. The trigger's delegated focus handler then calls ContextualModelView.render(), and render() always assigns trigger.textContent:
const trigger = this.$contextual.find('.trigger').get(0);
trigger.textContent = Drupal.t('@action @title configuration options', {
'@action': !isOpen ? this.strings.open : this.strings.close,
'@title': this.title,
});The label usually has not changed at that point. The assignment still replaces the button's text node between mousedown and mouseup, and Chromium then does not dispatch the click event. Event log of a failing click:
[pointerdown] trigger [focusin] trigger -> focus() + render() (textContent rewritten) [mouseup] trigger (no click event, menu stays closed; the region does get the "focus" class)
A second click works, because the trigger already has focus: no focusin fires, so the text is not rewritten during the press.
The Backbone-based implementation probably did not have this problem, because views only re-rendered when a model attribute actually changed. Since #3203920, render() runs on every click, blur, touchend and focus of the trigger.
Steps to reproduce
- Install Drupal (standard profile) and log in as a user with the "Use contextual links" permission.
- Place a block that contains links (for example a Views block listing content) in a region, so that the links are inside a
.contextual-region. - In a Chromium-based browser (Chrome/Edge), use Tab to move focus onto one of the links inside that block.
- Hover the block and click its contextual pencil once.
- Result: the menu does not open; the region gets the
focusclass, but.contextualdoes not getopen. A second click opens it. - The same happens when you first open another contextual menu and then click a different pencil.
Verified on Drupal 11.4.8 with Chrome on macOS.
Proposed resolution
Only write the trigger text in ContextualModelView.render() when it actually changes:
const trigger = this.$contextual.find('.trigger').get(0);
const newText = Drupal.t('@action @title configuration options', {
'@action': !isOpen ? this.strings.open : this.strings.close,
'@title': this.title,
});
if (trigger.textContent !== newText) {
trigger.textContent = newText;
}
trigger.setAttribute('aria-pressed', isOpen);The label still switches between "Open …" and "Close …" when the menu is toggled; that change happens after the click, so it does not interfere with it.
Remaining tasks
- Review the proposed fix.
- Decide whether a FunctionalJavascript test is feasible (it needs a real mousedown → focus → mouseup sequence; synthetic
.click()does not reproduce it). - Create an MR.
User interface changes
None. Contextual links open on the first click again.
Introduced terminology
None.
API changes
None.
Data model changes
None.
Release notes snippet
N/A
Issue fork drupal-3630665
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
quietone commentedHi, Issues for Drupal core should be targeted to the 'main' branch, our primary development branch. Changes are made on the main branch first, and are then back ported as needed according to the Core change policies. The version the problem was discovered on should be stated in the issue summary Problem/Motivation section. Thanks.