-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Open
Labels
Internationalization (i18n)Issues or PRs related to internationalization effortsIssues or PRs related to internationalization efforts[Feature] Link EditingLink components (LinkControl, URLInput) and integrations (RichText link formatting)Link components (LinkControl, URLInput) and integrations (RichText link formatting)[Type] BugAn existing feature does not function as intendedAn existing feature does not function as intended
Description
Description
This issue affects users who type using IME (Input Method Editor), commonly used for Japanese, Chinese, Korean, and other non-Latin languages. While entering text in the link URLInput component, search requests are fired during IME composition, before the user confirms the input. This leads to a poor editing experience and unnecessary API requests.
This appears to be an internationalization (i18n) usability issue that impacts many languages that rely on composition input.
We currently use a workaround plugin but believe this should be solved in core to improve accessibility and editor performance globally.
Expected Behavior
- Search should wait until IME composition ends (compositionend)
- UI should avoid firing network requests for intermediate characters
- Better user experience for Japanese, Chinese, and Korean users
Possible Solution (optional, for discussion)
- Skip search while composing (compositionstart/compositionend)
- Delay search until confirmed input
- Consider increasing debounce timing
- Add filter for debounce timing.
Step-by-step reproduction instructions
- Open the Block Editor
- Insert a Paragraph block
- Click the Link button
- Type Japanese text using IME, e.g. ほんだ → convert to ホンダ
- Open DevTools → Network
- Observe: multiple
/wp/v2/searchrequests sent during typing
Screenshots, screen recording, code snippet
urlinput.mp4
We currently use a workaround plugin but believe this should be solved in core to improve accessibility and editor performance globally.
/**
* IME tweaks and debounce control for Block Editor's Link UI.
*
* @package IncSearchDelay
*/
( function( wp ) {
'use strict';
if ( ! wp || ! wp.apiFetch || ! wp.domReady ) {
return;
}
// Get options from PHP(wp_localize_script)
const settings = window.incSearchDelaySettings || {
debounceMs: 500,
cacheDuration: 30000,
minSearchLength: 2
};
// IME status
let isComposing = false;
/**
* IME: Listen compositionstart/end events.
*/
wp.domReady( function() {
// compositionstart:
document.addEventListener( 'compositionstart', function( e ) {
if ( e.target.id && e.target.id.startsWith( 'url-input-control-' ) ) {
isComposing = true;
}
}, true );
// compositionend
document.addEventListener( 'compositionend', function( e ) {
if ( e.target.id && e.target.id.startsWith( 'url-input-control-' ) ) {
isComposing = false;
// After IME composition, fires input event and do search.
const event = new Event( 'input', { bubbles: true } );
e.target.dispatchEvent( event );
}
}, true );
} );
/**
* Cache store of the search results.
*/
const searchCache = new Map();
/**
* debounce timer
*/
const pendingRequests = new Map();
/**
* apiFetch middleware: Optimize requests for /wp/v2/search
*/
wp.apiFetch.use( function( options, next ) {
// Only affects for /wp/v2/search
if ( ! options.path || ! options.path.includes( '/wp/v2/search' ) ) {
return next( options );
}
// Do nothing while IME compositing.
if ( isComposing ) {
return Promise.resolve( [] );
}
const cacheKey = options.path;
const cached = searchCache.get( cacheKey );
// If a cache exists, return one.
if ( cached && ( Date.now() - cached.timestamp < settings.cacheDuration ) ) {
return Promise.resolve( cached.data );
}
// Cancel existing timer.
const existingTimer = pendingRequests.get( cacheKey );
if ( existingTimer ) {
clearTimeout( existingTimer.timer );
existingTimer.reject( new Error( 'Cancelled by new request' ) );
}
// debounce
return new Promise( function( resolve, reject ) {
const timer = setTimeout( function() {
pendingRequests.delete( cacheKey );
next( options )
.then( function( response ) {
// Cache results.
searchCache.set( cacheKey, {
data: response,
timestamp: Date.now()
} );
resolve( response );
} )
.catch( function( error ) {
// Ignore cancelled requests.
if ( error.message !== 'Cancelled by new request' ) {
reject( error );
}
} );
}, settings.debounceMs );
// Save a timer for cancelation.
pendingRequests.set( cacheKey, { timer, reject } );
} );
} );
} )( window.wp );Environment info
- WordPress 6.8.3
- Google Chrome
Please confirm that you have searched existing issues in the repo.
- Yes
Please confirm that you have tested with all plugins deactivated except Gutenberg.
- Yes
Please confirm which theme type you used for testing.
- Block
- Classic
- Hybrid (e.g. classic with theme.json)
- Not sure
inc2734 and megane9988
Metadata
Metadata
Assignees
Labels
Internationalization (i18n)Issues or PRs related to internationalization effortsIssues or PRs related to internationalization efforts[Feature] Link EditingLink components (LinkControl, URLInput) and integrations (RichText link formatting)Link components (LinkControl, URLInput) and integrations (RichText link formatting)[Type] BugAn existing feature does not function as intendedAn existing feature does not function as intended