Working on a responsive white paper for an open-source project.
Research has proven that this challenge has yet to be taken down clearly by anyone on the internet.
My original intention was to create a fully responsive A4 ratio container for each page and write text manually in each page element (this was a nice challenge on it's own and I succeeded to make the container the perfect ratio on any screen, but trying to make the text look the same on any screen seemed impossible), so I gave up.
IMPORTANT! I remembered that I want to be able to edit the content of the white paper directly from the HTML and allow a JS function to separate the content dynamically. Otherwise, each time I wanted to change something in one page and the text didn't fit anymore, I had to re-edit all the pages manually again, pushing all content down or up, in each page, which is mad.
So, now I want the text to be separated into pages automatically just like when you write in a text editor like Word, only you're writing HTML code.
There is extra difficulty coming from the fact that I am using images, iframes and other unusual elements inside the HTML, which means I cannot use SVG as a solution which would have been easy.
Now.
I prepared this codepen to make things easier: Responsive white paper (codepen)
So what I am trying to do is to somehow insert a break in the correct position, in my case the breaker is:
</page><page>
Using white-space: pre-line
or better, white-space: pre-wrap
seemed like a sensible first step in making things easier to edit. Using this CSS allows me to detect the exact place where each line of text ends.
Then, I have to take into account not only text-lines, but also images, iframes and maybe other type of elements the height of which I will not know, as they will be responsive.
My ultimate goal:
To insert the page separation elements in the correct place, making separate pages from raw HTML content where all pages are roughly the same if not the same height.
Possible solutions:
Maybe it's possible to work with the splitText function? Or the ES6 split() function? I will try.
Updates: This Solution is similar to what I need but I can't seem to make it work at the moment, will update.
I managed to eventually make this work, I did not use </page><page>
as a breaker as my HTML structure changed a bit to make things simpler.
There is lots of room to improve performance though! It's super slow! Same codepen as the question: https://codepen.io/lucian_apetrei/pen/QWOvwzm
Extra bonus! Is that I managed to create the perfect CSS calc to create a perfect responsive content inside an A4 ratio container that will look exactly the same on any screen, this is quite cool on it's own and if you want to use it, feel free!
HTML:
<div id="loader">Loading responsive white paper.<br><br><b>Please wait<br><hr><hr><hr></b></div>
<div id="white-paper">
<page id="page0">
<content>
<h1 class="top0">The shift to a Biosphere Consciousness</h2>
A new generation is rising and with it different views on how we define freedom, power, and community.
There is a basic change going on with the younger generation that is strange to older people. There is a change in the way you define “freedom”, the way your define “power”, and the way you define “community”, and these changes really suggest the real revolution.
For my generation and generations before me, freedom was very simple.
To be free is to be an autonomous agent, to be self-sufficient, to be independent, to not be beholden to others, to be an island to oneself so that one can have freedom as exclusivity.
<iframe class="fullWidth" src="https://www.youtube-nocookie.com/embed/h5Krh2ZmZkk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
For the younger generation that grew up on the Internet, autonomy is death. Being “an island to oneself” is death. Because for your generation, you ask the question, “How can I flourish to the full extent of my possibilities here on the planet?” And it is clear that your answer to that is I flourish to the extent that I am embedded in community after community where I can share my talents and those talents can benefit the network and come back to benefit myself. I am free because I have access and for you, freedom is not exclusivity. It is inclusivity.
The younger generation has a different sensibility about power which makes the older generation very nervous. We essentially have believed that power always has to be a pyramid. It goes from the top town. That is power. There is no other way to define power. It's a pyramid.
But young people that grow up on the Internet - it's strange because you grew up thinking that power has to do with the networks you're engaged in. For you, power is not vertical, it's lateral. For you, power is being enmeshed in network after network where you benefit each other. Open source!
<h2>And finally - I think most importantly</h2>
We are seeing a change in the way our younger generation perceives identity to community. I grew up in a nation state - we were very clear on community - that is each individual is born to be an autonomous agent and we are each sovereign. And each of us compete with other sovereign individuals in the marketplace for scarce resources in a zero sum game. Our nations represent us because they are our sovereigns and they represent all the millions of individual citizens who are sovereigns against other nations and each nation then competes with every other nation for scarce resources in the marketplace or the battlefield in a zero sum game.
Here's my question: Does anyone here believe that we are going to be able to address climate change and bring the human family together and take our responsibility for our fellow creatures and the Earth we live in with that worldview? Anybody?
What we're beginning to see with our younger generation, and I don't want to overstretch this but I am beginning to sense a shift from geopolitics to biosphere consciousness. The biosphere is that nineteen kilometers from the stratosphere to the ocean where all life and all the chemicals on the planet interact to maintain the ecosystems and the biology of the Earth.
These kids are learning ecological footprint. We actually have young people coming home and at dinner time they are asking their parents where the hamburger came from on the table. They are saying, “Did that hamburger come from a rainforest?” “Did they have to destroy the trees for four little inches of topsoil which only gives you three years of grazing so that cow could become my hamburger?” And when those trees are destroyed for the top soil to graze the cow for the hamburger, the kids are smart enough to understand that those trees harbor rare species of plant and animal life that only live in those canopies - they go extinct.
<img class="fullWidth" src="https://useruploads.socratic.org/VPqSrkhiSlmaLgJKbNZA_what-is-in-the-biosphere.png"> <xlines19xlines></xlines19xlines> And then they connect the dots. If the trees disappear for the soil to graze the cow for the hamburger, those trees are not there to absorb carbon dioxide from industrial emissions and that means the temperature of the planet goes up. They are beginning to understand that everything each of us does intimately affects some other human being, some other creature and the planet we live in. We live in an indivisible, biosphere community. There is no escape. This isn't just academic. Our well-being depends on the well-being of the whole system and all the creatures in it.
We all have to really come together. We've got one generation to lay down this new biosphere consciousness. Pass on this legacy so when your grandchildren look back at you, they can say you did the right thing. You helped replenish the planet, got us off carbon, and helped show our proper respect to generations not yet here including our fellow creatures.
We must begin to think, organize, and act from a biosphere consciousness if life as we know it will continue to exist. We are in the midst of the greatest transition upon which humanity has ever embarked.
<h3>How will you respond?</h3>
</content>
</page>
</div>
CSS:
html, body {background: #232323; margin: 0; padding: 0; height: 100%}
@import url('https://fonts.googleapis.com/css2?family=Dosis:wght@400;700&family=Roboto:wght@300;400;900&display=swap');
page {
padding: 2em 2em 0 2em; /* these are my page margins */
width: 69vw;
height: calc(69vw * 1.4142); /* This is for setting up the white-paper container to be at A4 ratio on any screen */
font-size: calc(69vw * 1.4142 / 54.0103); /* After days of tests and empty internet queries, I discovered the perfect font-size formula for responsive text on a A4 paper portrait ratio, on any screen, considering my above margins (padding); if you want to have smaller or bigger font sizes or for landscape ratios and margins, you also have to adjust the JavaScript variables */
line-height: 1;
font-family: Arial, sans-serif;
margin: 0 auto 1em auto;
background:#ffffff;
overflow: hidden;
position: relative;
box-sizing: border-box;
display: block
}
content {
position: relative;
display: inline;
font-weight: 400;
z-index: 12;
font-size: 100%;
line-height: 1.6;
white-space: break-spaces;
font-family: 'Roboto', Arial, sans-serif;
}
content h1, content h2, content h3, content h4 {
margin: 0 !important;
padding: 0 !important;
font-size: 1em;
font-weight: bold;
display: inline
}
content h1 {display: flex; align-items: center; white-space: pre-wrap; background: #db463e; color: #ffffff; width: 69vw; height: 2em; position: absolute; left: -2em; text-transform: uppercase;}
content h1:before {content: ''; display: block; width: 1em; height: 1em; background: #ffffff; margin: 0 0.5em 0 1em;}
.top0 {top: -2.19em}
content h2 {font-size: 1.23em}
content h3 {font-size: 1.12em}
content img, content iframe {width: 100%;}
content iframe {height: calc(69vw * 0.5625)}
.fullWidth {width: 69vw; position: absolute; left: -2em}
content div {display: inline}
/* For Mobile */
@media screen and (max-width: 981px) and (orientation: portrait) {
page {
width: 100vw;
height: calc(100vw * 1.4142); /* For Mobile */
font-size: calc(100vw * 1.4142 / 54.0103); /* For Mobile */
}
.fullWidth, content h1 {width: 100vw;}
content iframe {height: calc(100vw * 0.5625)}
}
#loader {text-align: center; font-size: 1em; width: 100vw; height: 100vh; background: rgba(0,0,0,.9); position: fixed; top: 0; z-index: 999; display: flex; justify-content: center; align-items: center; align-content: center; flex-direction: column; color: #ffffff; font-family: 'Roboto', Arial, sans-serif}
#loader b {font-size: 2em;}
#loader hr {display: inline-block; border: 0; width: 0.23em; height: 0.23em; background: #ffffff; margin: 0 0 0 0.23em; border-radius: 100%; animation: loaderDot 1.8s infinite ease-in-out}
#loader hr:nth-of-type(2) {animation-delay: 0.16s}
#loader hr:nth-of-type(3) {animation-delay: 0.32s}
@keyframes loaderDot {
0% {transform: scale(1)}
50% {transform: scale(0)}
100% {transform: scale(1)}
}
JS:
// Get a single first matching element
var $ = function (selector, parent) {
return (parent ? parent : document).querySelector(selector);
};
// Get all matching elements
var $$ = function (selector, parent) {
return (parent ? parent : document).querySelectorAll(selector);
};
document.addEventListener("DOMContentLoaded", function () {
var whitePaper = $('#white-paper');
var container = $('#page0');
var page0 = $('#page0 content');
var parsedContent = new DOMParser().parseFromString(page0.innerHTML, 'text/html'); // first, we are parsing all content like paragraphs and all other elements into an array using DOMParser
page0.innerHTML = ''; // let's Tabula Rasa so we can use the first page element to calculate each new addition into it
var parsedArray = [...parsedContent.body.childNodes]
.map(child => child.outerHTML || child.textContent);
var currentPage = 0;
var lastDiv = 0;
var pageRects = 0;
// a function to add the next page
var addPage = function() {
// add a new page
var newPage = document.createElement('page');
newPage.setAttribute("id", 'page' + currentPage);
var emptyContent = document.createElement('content');
newPage.appendChild(emptyContent);
whitePaper.appendChild(newPage);
}
// a function to add each parsed item to the current page in a div or to remove the item if it doesn't fit at the end of the page
var addRemoveItem = function(item, rects) {
// if we don't have a manual override for pageRects, get the actual rects nr
rects ? pageRects = rects : pageRects = $('#page' + currentPage + ' content').getClientRects().length;
let previousDivContent;
if (pageRects > 31) { // this will go through only once per new page
let previousDiv = $('#page' + currentPage + ' content div:last-of-type');
let previousDivId = previousDiv.id;
previousDivContent = previousDiv.innerHTML;
previousDiv.remove();
currentPage++;
addPage();
$('#page'+currentPage+ ' content').innerHTML += '<div id="'+previousDivId+'">'+previousDivContent+'</div>'; // this is the first item on the next page
}
// if we have a new element or word / punctuation and the pageRects are smaller than 34, we add the new div ahead of the previous one
var id = 'i' + lastDiv;
lastDiv++;
var newDiv = '<div id="'+id+'">'+item+'</div>';
$('#page'+currentPage+ ' content').innerHTML += newDiv;
// Debugging
//console.log('Page Rects: ' + pageRects + ' | Item: ' + item + ' | Prev end item: ' + previousDivContent);
}
// let's loop through all the content parsed with DOMParser (paragraphs, links, headings, images, iframes)
parsedArray.forEach((item, index) => {
if (item.match(/<\/?[a-z][\s\S]*>/)) {
// if the next item is an element
if (item.match(/xlines/)) {
let extraLines = item.split(/xlines/);
let lines = extraLines[1];
pageRects += parseInt(lines);
addRemoveItem(item, pageRects);
} else {
addRemoveItem(item);
}
} else {
// if the next item is a word / punctuation
var words = item.split(' ');
// loop through the words / punctuation
words.forEach((item, i) => {
addRemoveItem(item + ' ');
});
}
});
$('#loader').style.display = 'none';
});
IMPORTANT:
Using new lines in HTML works, but not all the time, in some situations, I don't know why, adding extra lines doesn't work properly, you can see this bug after the second image where I used <xlines15xlines></xlines15xlines>
as a placeholder, a manual rects count override for when I need extra lines that pushes content correctly.
PROBLEMS TO SOLVE: If anyone has any idea how to make it work faster, please contribute!