Skip to content

Instantly share code, notes, and snippets.

@loilo
Last active January 9, 2019 13:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loilo/ace13e897af9d976315918cbeab6acf8 to your computer and use it in GitHub Desktop.
Save loilo/ace13e897af9d976315918cbeab6acf8 to your computer and use it in GitHub Desktop.
Breaks elements out of their container
// This was originally designed for creating virtual print sheet layouts in HTML/CSS,
// breaking contents too long for one page to the next one.
// You can play around with it here: https://jsfiddle.net/Loilo/L3w8zjuf/3/
function internalBreak (breakItem, outmostContainer, breakDirection, childIsBroken = false) {
const container = breakItem.parentElement
const containerItems = Array.from(container.children)
// Move all items after `breakItem` to a new container
const nextContainer = container.cloneNode()
const breakItemIndex = containerItems.indexOf(breakItem)
for (let i = breakItemIndex + 1; i < containerItems.length; i++) {
const containerItem = containerItems[i]
nextContainer.appendChild(containerItem)
}
// Check if the container was broken
// If child is broken, parent is always broken as well
const isBroken = childIsBroken || (breakDirection === 'before'
? breakItem !== containerItems[0]
: breakItem !== containerItems[containerItems.length - 1]
)
// If container is broken, mark original and newly created with data-* attributes
if (isBroken) {
if (breakDirection === 'before') {
container.dataset.brokenAfter = ''
nextContainer.dataset.brokenBefore = ''
} else {
container.dataset.brokenBefore = ''
nextContainer.dataset.brokenAfter = ''
}
}
// If `breakItem`'s container is not the outmost possible one, recursively continue to break their parents
if (container !== outmostContainer) {
const newContainerContainer = internalBreak(container, outmostContainer, breakDirection, isBroken)
newContainerContainer.insertAdjacentElement('afterbegin', nextContainer)
} else {
outmostContainer.insertAdjacentElement('afterend', nextContainer)
}
return nextContainer
}
// Break after `breakItem`
function breakAfter (breakItem, outmostContainer) {
internalBreak(breakItem, outmostContainer, 'after')
}
// Break before `breakItem`
function breakBefore (breakItem, outmostContainer) {
const originalContainer = breakItem.parentElement
const nextContainer = internalBreak(breakItem, breakItem.closest('.page'), 'before')
nextContainer.insertAdjacentElement('afterbegin', breakItem)
// Remove empty containers
if (!originalContainer.children.length) {
let currentContainer = originalContainer
while (currentContainer.parentElement.children.length === 1) {
currentContainer = currentContainer.parentElement
}
currentContainer.remove()
}
}
<!-- The original DOM tree -->
<section class="page">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<article>
<p>Paragraph 3</p>
<p data-break-before>Paragraph 4</p>
<p>Paragraph 5</p>
</article>
</section>
<!-- Break the DOM! -->
<script>
const el = document.querySelector('[data-break-before]')
breakBefore(el, el.closest('.page')
</script>
<!-- This will result in the folllowing DOM tree -->
<section class="page" data-broken-after>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<article data-broken-after>
<p>Paragraph 3</p>
</article>
</section>
<section class="page" data-broken-before>
<article data-broken-before>
<p data-break-before>Paragraph 4</p>
<p>Paragraph 5</p>
</article>
</section>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment