diff --git a/CHANGELOG.md b/CHANGELOG.md index 66511a7365..26eca72588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ For advice on how to use these release notes, see [our guidance on staying up to ## Unreleased +### Fixes + +We've made fixes to GOV.UK Frontend in the following pull requests: + +- [#6449: Refactor Character count method to reduce repeated updates](https://github.com/alphagov/govuk-frontend/pull/6449) + ## v6.0.0-beta.1 (Beta breaking release) ### Breaking changes diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs index 37bb073f97..8f4c5599e2 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs @@ -25,6 +25,9 @@ export class CharacterCount extends ConfigurableComponent { /** @private */ $textarea + /** @private */ + count = 0 + /** @private */ $visibleCountMessage @@ -173,11 +176,19 @@ export class CharacterCount extends ConfigurableComponent { // When the page is restored after navigating 'back' in some browsers the // state of form controls is not restored until *after* the DOMContentLoaded // event is fired, so we need to sync after the pageshow event. - window.addEventListener('pageshow', () => this.updateCountMessage()) + window.addEventListener('pageshow', () => { + // If the current value of the textarea is the same as what's + // in the HTML, don't re-run. + if(this.$textarea.value !== this.$textarea.innerHTML) { + this.updateCount() + this.updateCountMessage() + } + }) // Although we've set up handlers to sync state on the pageshow event, init // could be called after those events have fired, for example if they are // added to the page dynamically, so update now too. + this.updateCount() this.updateCountMessage() } @@ -190,7 +201,7 @@ export class CharacterCount extends ConfigurableComponent { * @private */ bindChangeEvents() { - this.$textarea.addEventListener('keyup', () => this.handleKeyUp()) + this.$textarea.addEventListener('input', () => this.handleInput()) // Bind focus/blur events to start/stop polling this.$textarea.addEventListener('focus', () => this.handleFocus()) @@ -198,14 +209,15 @@ export class CharacterCount extends ConfigurableComponent { } /** - * Handle key up event + * Handle input event * * Update the visible character counter and keep track of when the last update * happened for each keypress * * @private */ - handleKeyUp() { + handleInput() { + this.updateCount() this.updateVisibleCountMessage() this.lastInputTimestamp = Date.now() } @@ -281,7 +293,7 @@ export class CharacterCount extends ConfigurableComponent { * @private */ updateVisibleCountMessage() { - const remainingNumber = this.maxLength - this.count(this.$textarea.value) + const remainingNumber = this.maxLength - this.count const isError = remainingNumber < 0 // If input is over the threshold, remove the disabled class which renders @@ -325,19 +337,20 @@ export class CharacterCount extends ConfigurableComponent { /** * Count the number of characters (or words, if `config.maxwords` is set) - * in the given text + * in the given text, and update the component-wide count * * @private - * @param {string} text - The text to count the characters of - * @returns {number} the number of characters (or words) in the text */ - count(text) { + updateCount() { + const text = this.$textarea.value + if (this.config.maxwords) { const tokens = text.match(/\S+/g) ?? [] // Matches consecutive non-whitespace chars - return tokens.length + this.count = tokens.length + return } - return text.length + this.count = text.length } /** @@ -347,7 +360,7 @@ export class CharacterCount extends ConfigurableComponent { * @returns {string} Status message */ getCountMessage() { - const remainingNumber = this.maxLength - this.count(this.$textarea.value) + const remainingNumber = this.maxLength - this.count const countType = this.config.maxwords ? 'words' : 'characters' return this.formatCountMessage(remainingNumber, countType) } @@ -392,7 +405,7 @@ export class CharacterCount extends ConfigurableComponent { } // Determine the remaining number of characters/words - const currentLength = this.count(this.$textarea.value) + const currentLength = this.count const maxLength = this.maxLength const thresholdValue = (maxLength * this.config.threshold) / 100