1- import { booleanHtmlAttributes } from '../../shared/constants'
2- import { toArray , includes } from '../../utils/array'
1+ import { booleanHtmlAttributes , commonDataAttributes } from '../../shared/constants'
2+ import { includes } from '../../utils/array'
3+ import { queryElements , getElementsKey } from '../../utils/elements.js'
34
45/**
56 * Updates meta tags inside <head> and <body> on the client. Borrowed from `react-helmet`:
@@ -9,11 +10,16 @@ import { toArray, includes } from '../../utils/array'
910 * @param {(Array<Object>|Object) } tags - an array of tag objects or a single object in case of base
1011 * @return {Object } - a representation of what tags changed
1112 */
12- export default function updateTag ( appId , { attribute, tagIDKeyName } = { } , type , tags , headTag , bodyTag ) {
13- const oldHeadTags = toArray ( headTag . querySelectorAll ( `${ type } [${ attribute } ="${ appId } "], ${ type } [data-${ tagIDKeyName } ]` ) )
14- const oldBodyTags = toArray ( bodyTag . querySelectorAll ( `${ type } [${ attribute } ="${ appId } "][data-body="true"], ${ type } [data-${ tagIDKeyName } ][data-body="true"]` ) )
15- const dataAttributes = [ tagIDKeyName , 'body' ]
16- const newTags = [ ]
13+ export default function updateTag ( appId , { attribute, tagIDKeyName } = { } , type , tags , head , body ) {
14+ const dataAttributes = [ tagIDKeyName , ...commonDataAttributes ]
15+ const newElements = [ ]
16+
17+ const queryOptions = { appId, attribute, type, tagIDKeyName }
18+ const currentElements = {
19+ head : queryElements ( head , queryOptions ) ,
20+ pbody : queryElements ( body , queryOptions , { pbody : true } ) ,
21+ body : queryElements ( body , queryOptions , { body : true } )
22+ }
1723
1824 if ( tags . length > 1 ) {
1925 // remove duplicates that could have been found by merging tags
@@ -29,64 +35,88 @@ export default function updateTag (appId, { attribute, tagIDKeyName } = {}, type
2935 }
3036
3137 if ( tags . length ) {
32- tags . forEach ( ( tag ) => {
38+ for ( const tag of tags ) {
3339 const newElement = document . createElement ( type )
34-
3540 newElement . setAttribute ( attribute , appId )
3641
37- const oldTags = tag . body !== true ? oldHeadTags : oldBodyTags
38-
3942 for ( const attr in tag ) {
4043 if ( tag . hasOwnProperty ( attr ) ) {
4144 if ( attr === 'innerHTML' ) {
4245 newElement . innerHTML = tag . innerHTML
43- } else if ( attr === 'cssText' ) {
46+ continue
47+ }
48+
49+ if ( attr === 'cssText' ) {
4450 if ( newElement . styleSheet ) {
4551 /* istanbul ignore next */
4652 newElement . styleSheet . cssText = tag . cssText
4753 } else {
4854 newElement . appendChild ( document . createTextNode ( tag . cssText ) )
4955 }
50- } else {
51- const _attr = includes ( dataAttributes , attr )
52- ? `data-${ attr } `
53- : attr
54-
55- const isBooleanAttribute = includes ( booleanHtmlAttributes , attr )
56- if ( isBooleanAttribute && ! tag [ attr ] ) {
57- continue
58- }
56+ continue
57+ }
58+
59+ const _attr = includes ( dataAttributes , attr )
60+ ? `data-${ attr } `
61+ : attr
5962
60- const value = isBooleanAttribute ? '' : tag [ attr ]
61- newElement . setAttribute ( _attr , value )
63+ const isBooleanAttribute = includes ( booleanHtmlAttributes , attr )
64+ if ( isBooleanAttribute && ! tag [ attr ] ) {
65+ continue
6266 }
67+
68+ const value = isBooleanAttribute ? '' : tag [ attr ]
69+ newElement . setAttribute ( _attr , value )
6370 }
6471 }
6572
73+ const oldElements = currentElements [ getElementsKey ( tag ) ]
74+
6675 // Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
6776 let indexToDelete
68- const hasEqualElement = oldTags . some ( ( existingTag , index ) => {
77+ const hasEqualElement = oldElements . some ( ( existingTag , index ) => {
6978 indexToDelete = index
7079 return newElement . isEqualNode ( existingTag )
7180 } )
7281
7382 if ( hasEqualElement && ( indexToDelete || indexToDelete === 0 ) ) {
74- oldTags . splice ( indexToDelete , 1 )
83+ oldElements . splice ( indexToDelete , 1 )
7584 } else {
76- newTags . push ( newElement )
85+ newElements . push ( newElement )
7786 }
78- } )
87+ }
88+ }
89+
90+ let oldElements = [ ]
91+ for ( const current of Object . values ( currentElements ) ) {
92+ oldElements = [
93+ ...oldElements ,
94+ ...current
95+ ]
96+ }
97+
98+ // remove old elements
99+ for ( const element of oldElements ) {
100+ element . parentNode . removeChild ( element )
79101 }
80102
81- const oldTags = oldHeadTags . concat ( oldBodyTags )
82- oldTags . forEach ( tag => tag . parentNode . removeChild ( tag ) )
83- newTags . forEach ( ( tag ) => {
84- if ( tag . getAttribute ( 'data-body' ) === 'true' ) {
85- bodyTag . appendChild ( tag )
86- } else {
87- headTag . appendChild ( tag )
103+ // insert new elements
104+ for ( const element of newElements ) {
105+ if ( element . hasAttribute ( 'data-body' ) ) {
106+ body . appendChild ( element )
107+ continue
88108 }
89- } )
90109
91- return { oldTags, newTags }
110+ if ( element . hasAttribute ( 'data-pbody' ) ) {
111+ body . insertBefore ( element , body . firstChild )
112+ continue
113+ }
114+
115+ head . appendChild ( element )
116+ }
117+
118+ return {
119+ oldTags : oldElements ,
120+ newTags : newElements
121+ }
92122}
0 commit comments