Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
feat(Shared scripts): Add DOM manipulation fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed Feb 18, 2020
1 parent 57795cc commit 57e1d0b
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 3 deletions.
24 changes: 21 additions & 3 deletions src/shared/js/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export function select(...args: (string | Document | Element)[]): Element[] {
* @param {...Element[]} children Additional child elements to add
* @returns {Element}
*/
export function create(spec = 'div', ...children: Element[]): Element {
if (spec.startsWith('<')) {
export function create(spec = 'div', ...children: (string | number | Element)[]): Element {
if (/^\s*</.test(spec)) {
const wrapper = document.createElement('div')
wrapper.innerHTML = spec
return wrapper.firstChild as Element
return wrapper.firstElementChild as Element
}

const tag = spec.match(/^[a-z0-9]+/i)?.[0] ?? 'div'
Expand Down Expand Up @@ -68,6 +68,24 @@ export function create(spec = 'div', ...children: Element[]): Element {
return elem
}

export function before(target: Element, insert: Element) {
target?.parentNode?.insertBefore(insert, target)
}

export function after(target: Element, ...insert: Element[]) {
const parent = target.parentNode
if (parent) {
insert.reverse().forEach(elem => parent.insertBefore(elem, target.nextSibling))
}
}

export function replace(target: string | Element, replacement: string | Element): void {
if (typeof target === 'string') target = select(target)[0]
if (typeof replacement === 'string') replacement = create(replacement)

target.parentNode?.replaceChild(replacement, target)
}

/**
* Type definition for something that can be used as a wrapper for
* HTML elements: an existing element, HTML for an element, a function
Expand Down
97 changes: 97 additions & 0 deletions src/shared/js/fixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { after, before, create, replace, select} from "./dom"

/**
* Makes DOM modifications to account for issues
* with the HTML generated by current version of Encoda
* that this repo is using.
*
* Over time these fixes should go into Encoda's `html` codec.
*/
export function fixes() {
personNames()
datePublished()
}

/**
* Encoda encodes the names of a `Person` as
*
* ```html
* <span itemprop="name" content="Sarel J. Fleishman">
* <span itemprop="givenName">Sarel J.</span>
* <span itemprop="familyName">Fleishman</span>
* </span>
* ```
*
* Note: the `itemprop` name with `content` ensures
* conformance with GSDTT.
*
* So that we can style the given names and family names,
* and consistent with other array properties, a better encoding would be:
*
* ```html
* <meta itemprop="name" content="Sarel J. Fleishman">
* <ol data-itemprop="givenNames">
* <li itemprop="givenName">Sarel</li>
* <li itemprop="givenName">J.</li>
* </ol>
* <ol data-itemprop="familyNames">
* <li itemprop="familyName">Fleishman</li>
* </ol>
* ```
*/
function personNames() {
select('[itemtype="http://schema.org/Person"] span[itemprop=name]').forEach(span => {
const name = span.getAttribute('content')
const givenNames = select(span, '[itemprop=givenName]').map(item => item.textContent).join(' ')
const familyNames = select(span, '[itemprop=familyName]').map(item => item.textContent).join(' ')

after(
span,
create(
'ol[data-itemprop=givenNames]',
...givenNames.split(/\s+/).map(givenName => create('li[itemprop=givenName]', givenName))
),
create(
'ol[data-itemprop=familyNames]',
...familyNames.split(/\s+/).map(familyName => create('li[itemprop=familyName]', familyName))
)
)
replace(
span,
`meta[itemprop=name][content=${name}]`
)
})
}


/**
* Encoda encodes a `CreativeWork.datePublished` as
*
* ```html
* <span>
* <meta itemprop="datePublished" content="2019-08-23">
* <time datetime="2019-08-23" itemscope="" itemtype="http://schema.org/Date">2019-08-23</time>
* </span>
* ```
*
* So that we can style the date, a better encoding would simply be:
*
* ```html
* <time datetime="2019-08-23" itemscope="" itemtype="http://schema.org/Date" itemprop="datePublished" >2019-08-23</time>
* ```
*
* Noting that for `<time>` elements the `datetime` attribute is used as the property value:
* https://www.w3.org/TR/microdata/#values
*/
function datePublished() {
select('span > meta[itemprop="datePublished"]').forEach(meta => {
const date = meta.getAttribute('content')
replace(
meta.parentElement as Element,
create(
`time[datetime=${date}][itemscope][itemtype="http://schema.org/Date"][itemprop=datePublished]`,
date as string
)
)
})
}

0 comments on commit 57e1d0b

Please sign in to comment.