Remove jQuery

* feature/no-jquery:
  More optimizations
  Really remove jQuery
  Refactor wrapify() completely without jQuery
  Begin refactor of wrapify
This commit is contained in:
Rico Sta. Cruz 2017-10-13 17:55:35 +08:00
commit 9e1dc45678
No known key found for this signature in database
GPG Key ID: CAAD38AE2962619A
14 changed files with 221 additions and 112 deletions

View File

@ -82,3 +82,4 @@ gtag('config','{{ site.data.google_analytics.id }}');
{% endif %}
<script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>
<script>(function(H){H.className=H.className.replace(/\bNoJs\b/,'WithJs')})(document.documentElement)</script>

View File

@ -1,5 +1,4 @@
// 3rd party libs
window.jQuery = window.$ = require('jquery')
window.Prism = require('prismjs')
// All the others

View File

@ -1,5 +1,6 @@
import onmount from 'onmount'
import $ from 'jquery'
import { nextUntil } from '../helpers/dom'
import matches from 'dom101/matches'
// Ensure that search-index is set first
import './searchable-item'
@ -9,16 +10,13 @@ import './searchable-item'
*/
onmount('[data-js-searchable-header]', function () {
const $this = $(this)
const $els = $this
.nextUntil('[data-js-searchable-header]')
.filter('[data-search-index]')
const els = nextUntil(this, '[data-js-searchable-header]')
.filter(el => matches(el, '[data-search-index]'))
const keywords = $els
.map(function () { return $(this).attr('data-search-index') })
.get()
const keywords = els
.map(n => n.getAttribute('data-search-index'))
.join(' ')
.split(' ')
$this.attr('data-search-index', keywords.join(' '))
this.setAttribute('data-search-index', keywords.join(' '))
})

61
_js/helpers/dom.js Normal file
View File

@ -0,0 +1,61 @@
import matches from 'dom101/matches'
/*
* Just like jQuery.append
*/
export function appendMany (el, children) {
children.forEach(child => { el.appendChild(child) })
}
/*
* Just like jQuery.nextUntil
*/
export function nextUntil (el, selector) {
const nextEl = el.nextSibling
return nextUntilTick(nextEl, selector, [])
}
function nextUntilTick (el, selector, acc) {
if (!el) return acc
const isMatch = matches(el, selector)
if (isMatch) return acc
return nextUntilTick(el.nextSibling, selector, [ ...acc, el ])
}
/*
* Just like jQuery.before
*/
export function before (reference, newNode) {
reference.parentNode.insertBefore(newNode, reference)
}
/*
* Like jQuery.children('selector')
*/
export function findChildren (el, selector) {
return [].slice.call(el.children)
.filter(child => matches(child, selector))
}
/**
* Creates a div
* @private
*
* @example
*
* createDiv({ class: 'foo' })
*/
export function createDiv (props) {
const d = document.createElement('div')
Object.keys(props).forEach(key => {
d.setAttribute(key, props[key])
})
return d
}

1
_js/helpers/noop.js Normal file
View File

@ -0,0 +1 @@
/* blank */

View File

@ -1,6 +1,7 @@
import wrapify from '../wrapify'
import ready from 'dom101/ready'
import onmount from 'onmount'
import addClass from 'dom101/add-class'
/**
* Behavior: Wrapping
@ -8,6 +9,9 @@ import onmount from 'onmount'
ready(() => {
const body = document.querySelector('[data-js-main-body]')
if (body) { wrapify(body) }
if (body) {
wrapify(body)
addClass(body, '-wrapified')
}
setTimeout(() => { onmount() })
})

View File

@ -22,17 +22,17 @@ exports[`h3 with class 1`] = `
<div
class="body -hello"
>
<p>
(install)
</p>
</div>
</div>
</div>
</div>
</div>
`;
@ -72,9 +72,14 @@ exports[`multiple h2s 1`] = `
<div
class="body"
>
<p>
(install)
</p>
</div>
</div>
<div
@ -86,23 +91,18 @@ exports[`multiple h2s 1`] = `
<div
class="body"
>
<p>
(usage)
</p>
</div>
</div>
</div>
</div>
<div
class="h2-section"
>
@ -135,9 +135,14 @@ exports[`multiple h2s 1`] = `
<div
class="body"
>
<p>
(first)
</p>
</div>
</div>
<div
@ -149,22 +154,17 @@ exports[`multiple h2s 1`] = `
<div
class="body"
>
<p>
(second)
</p>
</div>
</div>
</div>
</div>
</div>
`;
@ -204,9 +204,14 @@ exports[`simple usage 1`] = `
<div
class="body"
>
<p>
(install)
</p>
</div>
</div>
<div
@ -218,21 +223,16 @@ exports[`simple usage 1`] = `
<div
class="body"
>
<p>
(usage)
</p>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,63 +1,110 @@
import $ from 'jquery'
import matches from 'dom101/matches'
import addClass from 'dom101/add-class'
import { appendMany, nextUntil, before, findChildren, createDiv } from '../helpers/dom'
/*
/**
* Wraps h2 sections into h2-section.
* Wraps h3 sections into h3-section.
*
* @private
*/
export default function wrapify (root) {
const $root = $(root)
const $h2sections = groupify($root, {
tag: 'h2',
wrapper: '<div class="h2-section">',
body: '<div class="body h3-section-list" data-js-h3-section-list>'
})
// These are your H2 sections. Returns a list of .h2-section nodes.
const sections = wrapifyH2(root)
$h2sections.each(function () {
const $body = $(this).children('[data-js-h3-section-list]')
groupify($body, {
tag: 'h3',
wrapper: '<div class="h3-section">',
body: '<div class="body">'
})
// For each h2 section, wrap the H3's in them
sections.forEach(section => {
const bodies = findChildren(section, '[data-js-h3-section-list]')
bodies.forEach(body => { wrapifyH3(body) })
})
}
/*
* Groups stuff
/**
* Wraps h2 sections into h2-section.
* Creates and HTML structure like so:
*
* .h2-section
* h2.
* (title)
* .body.h3-section-list.
* (body goes here)
*
* @private
*/
export function groupify ($this, { tag, wrapper, body }) {
const $first = $this.children(':first-child')
let $result = $()
function wrapifyH2 (root) {
return groupify(root, {
tag: 'h2',
wrapperFn: () => createDiv({ class: 'h2-section' }),
bodyFn: () => createDiv({
class: 'body h3-section-list',
'data-js-h3-section-list': ''
})
})
}
/**
* Wraps h3 sections into h3-section.
* Creates and HTML structure like so:
*
* .h3-section
* h3.
* (title)
* .body.
* (body goes here)
*
* @private
*/
function wrapifyH3 (root) {
return groupify(root, {
tag: 'h3',
wrapperFn: () => createDiv({ class: 'h3-section' }),
bodyFn: () => createDiv({ class: 'body' })
})
}
/**
* Groups all headings (a `tag` selector) under wrappers like `.h2-section`
* (build by `wrapperFn()`).
* @private
*/
export function groupify (el, { tag, wrapperFn, bodyFn }) {
const first = el.children[0]
let result = []
// Handle the markup before the first h2
if (!$first.is(tag)) {
const $sibs = $first.nextUntil(tag)
$result = $result.add(wrap($first, null, $first.add($sibs)))
if (first && !matches(first, tag)) {
const sibs = nextUntil(first, tag)
result.push(wrap(first, null, [ first, ...sibs ]))
}
$this.children(tag).each(function () {
const $sibs = $(this).nextUntil(tag)
const $heading = $(this)
$result = $result.add(wrap($heading, $heading, $sibs))
// Find all h3's inside it
const children = findChildren(el, tag)
children.forEach(child => {
const sibs = nextUntil(child, tag)
result.push(wrap(child, child, sibs))
})
return $result
return result
function wrap ($pivot, $first, $sibs) {
const $wrap = $(wrapper)
$wrap.addClass($pivot.attr('class'))
$pivot.before($wrap)
function wrap (pivot, first, sibs) {
const wrap = wrapperFn()
const $body = $(body)
$body.addClass($pivot.attr('class'))
$body.append($sibs)
const pivotClass = pivot.className
if (pivotClass) addClass(wrap, pivotClass)
before(pivot, wrap)
if ($first) $wrap.append($first)
$wrap.append($body)
const body = bodyFn()
if (pivotClass) addClass(body, pivotClass)
appendMany(body, sibs)
return $wrap
if (first) wrap.appendChild(first)
wrap.appendChild(body)
return wrap
}
}

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang='en'><head>
<html class='NoJs' lang='en'><head>
{% include 2017/head.html %}
{% include 2017/article-schema.html page=page %}

View File

@ -69,3 +69,18 @@ a:hover {
}
}
}
/*
* Hide markdown before it's wrapped.
*/
html.WithJs .MarkdownBody {
& {
opacity: 0;
}
&.-wrapified {
opacity: 1;
transition: opacity 250ms linear;
}
}

View File

@ -3,6 +3,7 @@
text-align: center;
position: relative;
height: 130px;
overflow: hidden;
}
/* Prelude */

View File

@ -8,7 +8,6 @@ module.exports = {
app: './_js/app.js',
vendor: [
// Large 3rd-party libs
'jquery',
'prismjs',
// Prism plugins
@ -48,6 +47,12 @@ module.exports = {
}
]
},
resolve: {
alias: {
// Never bundle jQuery
'jquery': join(__dirname, '..', '_js/helpers/noop.js')
}
},
stats: 'minimal',
plugins: [
// Optimize module ID's for vendor chunks

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long