@@ -12,15 +12,7 @@ import { innerText, textContent } from 'domutils';
1212const hasOwn = Object . prototype . hasOwnProperty ;
1313const rspace = / \s + / ;
1414const dataAttrPrefix = 'data-' ;
15- /*
16- * Lookup table for coercing string data-* attributes to their corresponding
17- * JavaScript primitives
18- */
19- const primitives : Record < string , unknown > = {
20- null : null ,
21- true : true ,
22- false : false ,
23- } ;
15+
2416// Attributes that are booleans
2517const rboolean =
2618 / ^ (?: a u t o f o c u s | a u t o p l a y | a s y n c | c h e c k e d | c o n t r o l s | d e f e r | d i s a b l e d | h i d d e n | l o o p | m u l t i p l e | o p e n | r e a d o n l y | r e q u i r e d | s c o p e d | s e l e c t e d ) $ / i;
@@ -475,17 +467,15 @@ interface DataElement extends Element {
475467 * Sets the value of a data attribute.
476468 *
477469 * @private
478- * @param el - The element to set the data attribute on.
470+ * @param elem - The element to set the data attribute on.
479471 * @param name - The data attribute's name.
480472 * @param value - The data attribute's value.
481473 */
482474function setData (
483- el : Element ,
475+ elem : DataElement ,
484476 name : string | Record < string , unknown > ,
485477 value ?: unknown
486478) {
487- const elem : DataElement = el ;
488-
489479 elem . data ??= { } ;
490480
491481 if ( typeof name === 'object' ) Object . assign ( elem . data , name ) ;
@@ -494,62 +484,77 @@ function setData(
494484 }
495485}
496486
487+ /**
488+ * Read _all_ HTML5 `data-*` attributes from the equivalent HTML5 `data-*`
489+ * attribute, and cache the value in the node's internal data store.
490+ *
491+ * @private
492+ * @category Attributes
493+ * @param el - Element to get the data attribute of.
494+ * @returns A map with all of the data attributes.
495+ */
496+ function readAllData ( el : DataElement ) : unknown {
497+ for ( const domName of Object . keys ( el . attribs ) ) {
498+ if ( ! domName . startsWith ( dataAttrPrefix ) ) {
499+ continue ;
500+ }
501+
502+ const jsName = camelCase ( domName . slice ( dataAttrPrefix . length ) ) ;
503+
504+ if ( ! hasOwn . call ( el . data , jsName ) ) {
505+ el . data ! [ jsName ] = parseDataValue ( el . attribs [ domName ] ) ;
506+ }
507+ }
508+
509+ return el . data ;
510+ }
511+
497512/**
498513 * Read the specified attribute from the equivalent HTML5 `data-*` attribute,
499- * and (if present) cache the value in the node's internal data store. If no
500- * attribute name is specified, read _all_ HTML5 `data-*` attributes in this
501- * manner.
514+ * and (if present) cache the value in the node's internal data store.
502515 *
503516 * @private
504517 * @category Attributes
505518 * @param el - Element to get the data attribute of.
506519 * @param name - Name of the data attribute.
507- * @returns The data attribute's value, or a map with all of the data
508- * attributes.
520+ * @returns The data attribute's value.
509521 */
510- function readData ( el : DataElement , name ?: string ) : unknown {
511- let domNames ;
512- let jsNames ;
513- let value ;
522+ function readData ( el : DataElement , name : string ) : unknown {
523+ const domName = dataAttrPrefix + cssCase ( name ) ;
524+ const data = el . data ! ;
514525
515- if ( name == null ) {
516- domNames = Object . keys ( el . attribs ) . filter ( ( attrName ) =>
517- attrName . startsWith ( dataAttrPrefix )
518- ) ;
519- jsNames = domNames . map ( ( domName ) =>
520- camelCase ( domName . slice ( dataAttrPrefix . length ) )
521- ) ;
522- } else {
523- domNames = [ dataAttrPrefix + cssCase ( name ) ] ;
524- jsNames = [ name ] ;
526+ if ( hasOwn . call ( data , name ) ) {
527+ return data [ name ] ;
525528 }
526529
527- for ( let idx = 0 ; idx < domNames . length ; ++ idx ) {
528- const domName = domNames [ idx ] ;
529- const jsName = jsNames [ idx ] ;
530- if (
531- hasOwn . call ( el . attribs , domName ) &&
532- ! hasOwn . call ( ( el as DataElement ) . data , jsName )
533- ) {
534- value = el . attribs [ domName ] ;
535-
536- if ( hasOwn . call ( primitives , value ) ) {
537- value = primitives [ value ] ;
538- } else if ( value === String ( Number ( value ) ) ) {
539- value = Number ( value ) ;
540- } else if ( rbrace . test ( value ) ) {
541- try {
542- value = JSON . parse ( value ) ;
543- } catch ( e ) {
544- /* Ignore */
545- }
546- }
530+ if ( hasOwn . call ( el . attribs , domName ) ) {
531+ return ( data [ name ] = parseDataValue ( el . attribs [ domName ] ) ) ;
532+ }
533+
534+ return undefined ;
535+ }
547536
548- ( el . data as Record < string , unknown > ) [ jsName ] = value ;
537+ /**
538+ * Coerce string data-* attributes to their corresponding JavaScript primitives.
539+ *
540+ * @private
541+ * @category Attributes
542+ * @returns The parsed value.
543+ */
544+ function parseDataValue ( value : string ) : unknown {
545+ if ( value === 'null' ) return null ;
546+ if ( value === 'true' ) return true ;
547+ if ( value === 'false' ) return false ;
548+ const num = Number ( value ) ;
549+ if ( value === String ( num ) ) return num ;
550+ if ( rbrace . test ( value ) ) {
551+ try {
552+ return JSON . parse ( value ) ;
553+ } catch ( e ) {
554+ /* Ignore */
549555 }
550556 }
551-
552- return name == null ? el . data : value ;
557+ return value ;
553558}
554559
555560/**
@@ -650,8 +655,8 @@ export function data<T extends AnyNode>(
650655 dataEl . data ??= { } ;
651656
652657 // Return the entire data object if no data specified
653- if ( ! name ) {
654- return readData ( dataEl ) ;
658+ if ( name == null ) {
659+ return readAllData ( dataEl ) ;
655660 }
656661
657662 // Set the value (with attr map support)
@@ -664,9 +669,6 @@ export function data<T extends AnyNode>(
664669 } ) ;
665670 return this ;
666671 }
667- if ( hasOwn . call ( dataEl . data , name ) ) {
668- return dataEl . data [ name ] ;
669- }
670672
671673 return readData ( dataEl , name ) ;
672674}
0 commit comments