Splitting 'contenteditable' article into vertical pages with header and footer - javascript

I am working on a basic WYSIWYG word processor dealing with lengthy documents. Currently I am taking a HTML article element filled with approximately 5000 divs and applying some jQuery code to style and format it. 5000 is likely to be the absolute maximum.
Firstly, I use regex to apply CSS and this is working nicely at about
107ms.
Secondly, I iterate over the divs, storing their height and
every 1000px, inserting some HTML to act as a page break (the
pagination is vertical - imagine pages in MS Word). The second part
takes around 1200ms.
For initial page loading, I'm okay with that. Unfortunately, the article element needs to be contenteditable.
When a user changes the content of the article, I can simply apply my regex on the div they have changed (0-1ms) and that is no problem, however, any change to the content means (a possible) altering of the height between page breaks, and therefore necessitates repagination.
Having the browser lock up for a second every time the user hits return, delete, ends up on a new line or numerous other additional events that would alter the pagination is obviously not practical. While performance is better when making changes nearer the end of document, the user requires the same level of responsiveness when making changes to page one.
I need to address this performance issue and make the UI responsive when repaginating.
Here's an example of the HTML before the JS code is run on it:
<article contenteditable="true">
<header contenteditable="false"><h1>Header Text</h1></header>
<div>Content</div>
<div>Content</div>
<div>Content</div>
...
<footer contenteditable="false"><h3>Footer Text</h3></footer>
</article>
And after the JS code is run:
<article contenteditable="true">
<header contenteditable="false"><h1>Header Text</h1></header>
<div class="regex-set-class">Content</div>
<div class="regex-set-class">Content</div>
<div class="regex-set-class">Content</div>
<section contenteditable="false">
<h3>Footer Text</h3>
<figure><hr><hr></figure>
<h1>Header Text</h1>
<h2>Page Number</h2>
</section>
<div class="regex-set-class">Content</div>
<div class="regex-set-class">Content</div>
<div class="regex-set-class">Content</div>
...
<footer contenteditable="false"><h3>Footer Text</h3></footer>
</article>
And here is the JS code performing the pagination:
function PaginateDocument()
{
// Variables
var height = 0;
var page_count = 1;
// Remove existing page breaks
$("section").remove();
// Iterate over each div elements
$("div").each(function()
{
// Check whether page is too long
if (height + $(this).outerHeight(true) > 1000)
{
// Create page break
$(this).before("<section contenteditable=\"false\"><h3>" + page_footer + "</h3><figure><hr /><hr /></figure><h1>" + page_header + "</h1><h2>" + page_count + ".</h2></section>");
// Adjust variables
page_count++; height = 0;
}
// Increment current page height
height += $(this).outerHeight(true);
});
}

Related

Best ways to display width of multiple elements as text

Hi I'm looking at ways to specify the widths of a large number of objects on a page AND have each object's width displayed within it as text. The main aim is to avoid having a reference to the width anywhere (whether in the HTML, CSS or JS) more than once but I need potentially thousands of these objects on one page (currently I specify the width of the div and a text within it - too inefficient!).
So far I have this: https://jsfiddle.net/ghostfood/d6acdhq6/17/
<body onLoad="myFunction()">
<div id="object1" class="voteshare1" style="width:40.6%;">
This one is <span id="percentage1"></span></div>
<div id="object2" class="voteshare2" style="width:20.4%;">
This one is <span id="percentage2"></span></div>
<div id="object3" class="voteshare3" style="width:10.2%;">
This one is <span id="percentage3"></span></div>
<script>
function myFunction() {
var x1 = document.getElementById("object1").style.width;
var x2 = document.getElementById("object2").style.width;
var x3 = document.getElementById("object3").style.width;
document.getElementById("percentage1").innerHTML = x1;
document.getElementById("percentage2").innerHTML = x2;
document.getElementById("percentage3").innerHTML = x3;
}
</script>
</body>
The width must be a percentage but ideally would not include the percentage symbol in the displayed text (not sure how it's doing that as this is an example I found online then modified a bit - I do not know JS very well).
I've looked at D3 and amcharts for this briefly but I'm not sure they're best for handling hundreds of small stacked bar charts on one page and with lots of CSS control which is what I need. I may well be wrong!
Summary: Help me figure out a more efficient way of getting and displaying the (percentage) width (as set manually in HTML or JS and within a range of 10% to 100%) of an object within it as text (the caveat being that I need to do this for thousands of small objects on one page).
Set a common class to all divs that you want to get the width.
Select all of then with getElementsByClassName()
Loop through each one getting its width.
find the children span and add the string to it.
See below
function myFunction() {
var elements = document.getElementsByClassName("voteshare");
for (var i = 0; i < elements.length; i++){
var thisElement = elements[i];
var thisWidth = thisElement.style.width.toString();
thisElement.children[0].textContent += thisWidth;
}
}
<body onLoad="myFunction()">
<div id="object1" class="voteshare" style="width:40.6%;">
<span id="percentage1">This one is </span>
</div>
<div id="object2" class="voteshare" style="width:20.4%;">
<span id="percentage2">This one is </span>
</div>
<div id="object3" class="voteshare" style="width:10.2%;">
<span id="percentage3">This one is </span>
</div>
</body>
The problem with the JS is that you were referencing to object1 but the name of the <div> is object.
When a browser encounters an error, the execution of the script stops. That means that none of your code was running because the error was on the first line (of the function code).

loop through div continuously w/ auto scroll

When a page loads with the div below I want the page to start auto scrolling and keep looping through the content over and over again. To see an example of what I am trying to achieve go here
<div id="contentwrapper">
<div class="post">some content</div>
<div class="post">some content</div>
<div class="post">some content</div>
<div class="post">some content</div>
<div class="post">some content</div>
<div class="post">some content</div>
</div>
Any help will be greatly appreciated as I have tried almost everything with no success.
You can change the scroll position of an element by setting the scrollTop property. To update it continuously, you could try:
var scrollDistancePerSecond = 50; // Scroll 50px every second.
var scrollDistancePerAnimationFrame = Math.ceil(scrollDistancePerSecond / 60); // Animate at 60 fps.
var wrapper = document.getElementById('contentwrapper');
autoScroll(wrapper);
function autoScroll(element){
if (element.scrollTop < element.scrollHeight)
window.requestAnimationFrame(autoScroll.bind(null,element));
element.scrollTop += scrollDistancePerAnimationFrame;
}
Live example here.
As for the continuous loop, you could wrap the .post elements in an inner wrapper (e.g., a <div id="inner-wrap">. Then you could duplicate the content by appending a clone of the inner-wrapped elements inside the outer wrapper.
var innerWrap = document.getElementById('inner-wrap');
var clone = document.createElement('div');
clone.innerHTML = innerWrap.innerHTML;
wrapper.append(clone);
Then, inside your animation function, you can check whether the wrapper's scrollTop has reached innerWrap.scrollHeight. If it has, you can jump back to the top! It'll take some fine-tuning to get a seamless effect.
Update
The linked page does not use scrollTop, but animates the y component of the CSS transform translate3d on the wrapper <div>. You might get more performant results using CSS transitions or animations over JavaScript. Here is an example in pure CSS3.

What's the right way to run a javascript function without getting the object to "jump" right after the page loads

I have simple page with an object that gets a "top" property from the javascript.
How do I run the function without getting things on my page to "jump" ?
<script type="text/javascript">
function changeHeight () {
//Gets height
var h = window.innerHeight;
//alert(h);
console.log(h);
var categories = document.getElementById("cat").offsetHeight;
//alert(categories);
var x = 0.32 * categories;
var catTop = h - x;
//Gets cats
document.getElementById("cat").style.top = catTop+"px";
}
</script>
</head>
<body onload="changeHeight()" onresize="changeHeight()">
<div class="main">
<div class="cat" id="cat"></div>
</div>
</body>
</html>
I used "onload" on the tag to run the function. Which I know that's not so good.
The object jumps because you move it after the DOM has been rendered. That's what onload does: Make sure the DOM is complete and all loading/rendering has happened.
There are two solutions:
Put the script element after the node.
Use CSS to position the element
The first solution looks like this:
<div class="main">
<div class="cat" id="cat"></div>
<script>...</script>
</div>
At the time when the script is executed, the necessary DOM nodes are there. Unless your layout is complex, the offsets should be correct at this time. Note that many browsers start rendering while the page is loading. So there still might be a jump but less often, depending on the complexity of the page, browser optimizations, etc.
The second solution is to wrap your element in a container where you set the margin/padding until the cat element is naturally positioned correctly. The 0.32 would be translated to 32%. You need another element around it which has the correct height but which isn't visible.
To final solution should give body height: 100%, then add two containers inside. One for the content and the other to position the cat element. You will need to play with position style. Then
#cat { top: 32% }
should do the trick.

Jquery Mobile ui-grid

Just a quick jQuery Mobile question.
I am working with ui-grid-a (for the basic case of 50/50 split) and I can populate and render the grid without issue. However in some cases there are fewer items than the content size and I was curious if it was possible to have the grid not wrap the content, but instead fill the available space in the div.
I have tried a few different methods.
1) Basic configuration no modification- the ui-grid seems to wrap the content inspection shows the height on the data-role=content is much smaller than the page. Thus the grid is "filling" the content.
2) As a result I then worked to expand the content. Using some JS fiddle code (below) I was able to set a height to the content (and inspection confirmed this) however the grid remains only a fraction of the content area
jQuery code in .ready() to adjust the content size to fill page
var screen = $.mobile.getScreenHeight();
var header = $(".ui-header").hasClass("ui-header-fixed") ? $(".ui-header").outerHeight() - 1 : $(".ui-header").outerHeight();
var footer = $(".ui-footer").hasClass("ui-footer-fixed") ? $(".ui-footer").outerHeight() - 1 : $(".ui-footer").outerHeight();
var contentCurrent = $(".ui-content").outerHeight() - $(".ui-content").height();
var content = screen - header - footer - contentCurrent;
$(".ui-content").height(content);
3) I tried binding this to the page load thinking that maybe the grid was calculated before the Javascript on ready fired. But no luck.
Any suggestions would be very welcome. I am not restricted to jQuery, I've tried many CSS options however those styles don't seem to help when using the custom jQuery Mobile UI (Content/Page)
Thank you in advance,
As requested a sample markup mirroring my own (I have some addition divs within this that are popups, but this represents the main content)
<div data-role="page" id="home" data-theme="b">
<div data-role="header">
<h1>Home</h1>
</div>
<div data-role="content">
<div class="ui-grid-a">
<div class="ui-block-a">
Test
</div>
<div class="ui-block-b">
Test
</div>
<div class="ui-block-a">
Test
</div>
<div class="ui-block-b">
Test
</div>
<div class="ui-block-a">
Test
</div>
<div class="ui-block-b">
Test
</div>
</div>
</div>
</div>
First I want to give a big thank you to #Omar for his help in creating this solution.
There are a couple of components that factor into this problem. First the page load sequence as to when the element heights are calculated for header and footer and content. To account for this you can bind to the pagebeforecreate event:
$(document).bind("pagebeforecreate", function (e, data) {
$(document).on("pagecontainertransition", contentHeight);
$(window).on("throttledresize orientationchange", contentHeight);
});
Now I will discuss how to resize a grid dynamically
Next you must account for the screen size of the device and calculate the appropriate size for each of the row elements using the code
var screen = $.mobile.getScreenHeight();
You must then calculate the size of the content and the size available for that content
var header = $(".ui-header").hasClass("ui-header-fixed") ? $(".ui-header").outerHeight() - 1 : $(".ui-header").outerHeight();
var footer = $(".ui-footer").hasClass("ui-footer-fixed") ? $(".ui-footer").outerHeight() - 1 : $(".ui-footer").outerHeight();
var contentCurrent = $(".ui-content").outerHeight() - $(".ui-content").height();
var content = screen - header - footer - contentCurrent;
From here you can calculate the grid size
var numRows = 3;
$(".ui-content .ui-block-a .ui-btn,.ui-content .ui-block-b .ui-btn").height((content / numRows) - 32);
}
Next as you may have more than one grid
In this case you can add a descending selector to specify which grid you would like to resize
$("#GridID.ui-content .ui-block-a .ui-btn, #GridID.ui-content .ui-block-b .ui-btn").height((content / numRows) - 32);
Finally since this event binds to page only once you have to resize all grids at the same time, however the grids may be on different pages and you must select those sizes as each page may have different sizes of content
var header = $("#pageID .ui-header").hasClass("ui-header-fixed") ? $("#pageID .ui-header").outerHeight() - 1 : $("#pageID .ui-header").outerHeight();
var footer = $("#pageID .ui-footer").hasClass("ui-footer-fixed") ? $("#pageID .ui-footer").outerHeight() - 1 : $("#pageID .ui-footer").outerHeight();
var contentCurrent = $("#pageID .ui-content").outerHeight() - $("#pageID .ui-content").height();
var content = screen - header - footer - contentCurrent;
Here is a fiddle to demonstrate the full functionality
http://jsfiddle.net/m6bbbpg1/
Hopefully this will help someone else as this seems to be a fairly common task when creating generic mobile webpages

Automatically adjust div height to window size, but no smaller than children

I could use a little help with the following issue. What I've got is a fairly basic one-page website. Stripped down, it's structure looks pretty much like this:
<div class="full_height" id="home">
<div class="container">
<div class="row">
<div class="span8">
CONTENT GOES HERE
</div>
</div>
</div>
</div>
This div structure repeats 5 times, with consecutive id's [home, second, third, fourth, fifth]. The problem I'm having is that I'd like the .full_height containers to be the same size as the window upon loading the site and resize along with the window size upon resizing. In addition, the div's size should never become any smaller than it's children.
My current attempt looks like this:
$(window).on("resize load", resizeWindow);function resizeWindow( e ) {
var newWindowHeight = $(window).height()-160;
var idSelector = ["home","second","third","fourth","fifth"];
for (var i = 0; i < idSelector.length; i++) {
var element = document.getElementById(idSelector[i])
if( element.offsetHeight < element.scrollHeight){
$("#"+idSelector[i]).css("height", "auto" );
}
else{
$("#"+idSelector[i]).css("height", newWindowHeight );
}
}
}
Now, the script above sort of does what I'm looking for but with some hick-ups.
i) Upon loading the site it only adjusts the div height to the windows size, not meeting the children requirement. It only does this upon resizing the window.
ii) While resizing the window, the div height will flicker back and forth between "auto" and window size. Depending on when you stop resizing it will take either of the two sizes as its height.
Any ideas?
You need to start with 100% height all the way up the HTML tree:
html, body, #full_height {
height:100%;
}

Categories