I'm trying to understand if there is an efficient way to have a preview of potentially long HTML content.
For example, I have a very long article with markup and tables, with tables sometimes being the very first thing in the article. I want to be able to efficiently render the first 300px (in height), which upon a click I will display in a scrollable dialog.
My current solution was to strip all HTML tags and just render x number of lines in plain text, however this solution is not acceptable as it fails pretty badly on tables.
On a page I might have multiple elements with long html content previews, so I would prefer some other approach than just have CSS restricted height blocks, but with all content rendered by the browser, as I assume this will be quite inefficient.
Related
I'd like to style a lengthy HTML document I intend to print (/ convert to a PDF) so that, on a particular physical page, the body is styled in one way, while on every other page, it is styled another way. Specifically, I want the page which contains a particular paragraph (say <p id="dramatic-scene">) to have a different background image from the other pages.
(The document itself is not structured for particular pages - instead of having a div for each page, we have a single container for the full content of the piece. If it were, we could probably just make the div for the page cover the entire dimensions of the page and give it the image - but I'm hoping to avoid having a rigid structure for the pages.)
In Selectors Level 4, there's a :has() pseudo-class (although part of a working draft, so it's not supported anywhere yet). Is there anything similar which could specify a page to apply CSS to? Something like #media print and (page-has: #dramatic-scene) {}. I doubt there is - some quick searching didn't find anything - but I figure it's worth asking here.
More realistically, is there any way I could simulate that with JavaScript? That'd be a fine solution for my use case. I couldn't find any info about that in searching though - any JS discussion related to printing seems to be about making buttons to print, swapping screen for print stylesheet before printing, etc, not making the page style react to the visible content.
We use jsPDF + html2canvas Javascript libraries to export HTML to PDF without the need for Native Browser support. Some of our big corporate customers use browsers which do not have Print to PDF functionality.
The current solution works pretty well, it creates a container DIV element with "overflow:hidden" and positions the HTML that is being exported such that only 1 page is visible, since we know exactly how many pixels wide/tall the page should be, then we use the library to export the view at that moment, then reposition things for the next page by scrolling down (actually we use a negative top margin, since html2canvas doesn't respect the "scrollTop" property).
The problem, though, is that this solution does not handle well at the borders of the pages. Images and text will get cut in the middle, and then continues on the next page. The end result looks weird, but at least there is nothing missing.
Is there a way to prepare this HTML in such a way that it will behave nicely at the borders? I am given an HTML element that when placed into an element of pageWidth pixels wide needs to be cut into pieces of exactly pageHeight pixels tall. I can modify the HTML any way that I want, b
I feel like there should be some solution, since I've noticed using the native PDF Print on Chrome solves this somehow because the output looks fine there - it somehow moves images around so it doesn't get cut off.
Here is a sample JS that I created that demonstrates how my code will create multiple pages.
http://jsfiddle.net/56u9Lxgt/
Basically, you have to check the actual page size always before adding new content.
In this link the answer:
jsPDF multi page PDF with HTML renderer
I have a question on a big # of dom elmenets and performance.
Let's say I have 6000 dom elements on a page and the number of the elements can be increased as a user interact with the page (user scrolls to create a new dom element) like twitter.
To improve the performance of the page, I can think of only two things.
set display to none to invisible items to avoid reflow
remove invisible items from the dom then re-add them as needed.
Are they any other ways of improving a page with a lot of dom elements?
We had to deal with a similar problem on FoldingText. As the document grew larger, more line elements and associated span elements were created. The browser engine just seemed to choke, and so a better solution needed to be found.
Here's what we did, may or may not be useful for your purposes:
Visualize the entire page as a long document, and the browser viewport as the lens for a specific part of the long document. You really only have to show the part within the lens.
So the first part is to calculate the visible view port. (This depends on how your elements are placed, absolute / fixed / default)
var top = document.scrollTop;
var width = window.innerWidth;
var height = window.innerHeight;
Some more resources to find a more cross-browser based viewport:
How to get the browser viewport dimensions?
Cross-browser method for detecting the scrollTop of the browser window
Second, you need a data structure to know which elements are visible in that area
We already had a balanced binary search tree in place for text editing, so we extended it to manage line heights too, so this part for us was relatively easy. I don't think you'll need a complex data structure for managing your element heights; a simple array or object might do fine. Just make sure you can query heights and dimensions easily on it. Now, how would you get the height data for all your elements. A very simple (but computationally expensive for large amounts of elements!)
var boundingRect = element.getBoundingClientRect()
I'm talking in terms of pure javascript, but if you're using jQuery $.offset, $.position, and methods listed here would be quite helpful.
Again, using a data structure is important only as a cache, but if you want, you could do it on the fly (though as I've stated these operations are expensive). Also, beware of changing css styles and calling these methods. These functions force redraw, so you'll see a performance issue.
Lastly, just replace the elements offscreen with a single, say <div> element with calculated height
Now, you have heights for all the elements stored in your Data structure, query all the elements that lie before the visible viewport.
Create a <div> with css height set (in pixels) to the sum of the element heights
Mark it with a class name so that you know its a filler div
Remove all the elements from the dom that this div covers
insert this newly created div instead
Repeat for elements that lie after the visible viewport.
Look for scroll and resize events. On each scroll, you will need to go back to your data structure, remove the filler divs, create elements that were previously removed from screen, and accordingly add new filler divs.
:) It's a long, complex method, but for large documents it increased our performance by a large margin.
tl;dr
I'm not sure I explained it properly, but the gist of this method is:
Know the vertical dimensions of your elements
Know the scrolled view port
Represent all off-screen elements with a single div (height equal to the sum of all element heights it covers for)
You will need two divs in total at any given time, one for elements above the visible viewport, one for elements below.
Keep track of the view port by listening for scroll and resize events. Recreate the divs and visible elements accordingly
No experience myself with this, but there are some great tips here: http://engineering.linkedin.com/linkedin-ipad-5-techniques-smooth-infinite-scrolling-html5
I had a look at Facebook and they don't seem to do anything in particular on Firefox. As you scroll down, the DOM elements at the top of the page don't change. Firefox's memory usage climbs to about 500 meg before Facebook doesn't allow you to scroll further.
Twitter appears to be the same as Facebook.
Google Maps is a different story - map tiles out of view are removed from the DOM (although not immediately).
It's 2019. The question is really old, but I think it is still relevant and interesting and maybe something changed as of today, as we all now also tend to use React JS.
I noticed that Facebook's timeline seems to use clusters of content which is hidden with display: none !important as soon as the cluster goes out of view, so all the previously rendered elements of the DOM are kept in the DOM, it's just that those out of view are hidden with display: none !important.
Also, the overall height of the hidden cluster is set to the parent div of the hidden cluster.
Here are some screenshots I've made:
As of 2019, what do you think about this approach? Also, for those who use React, how could it be implemented in React? It would be great to receive your opinions and thoughts regarding this tricky topic.
Thank you for the attention!
I'm trying to build a word processor web app, similar to Google Docs. I've started using Mercury Editor (which relies on the contentEditable attribute) and I've created an editable div element that looks like a paper page (as Google Docs does).
Now the big problem is how to deal with several pages, i.e., how to detect when text (or other content) overflows the page height and then create a new page with the content split. There are a few scenarios when this could ocurr:
A user type a break line at the end of the page. A new page should be created.
A user is typing a word and he reaches the end of the page. A new page should be created and that word should be moved to the new page.
A user pastes some large text and it doesn't fit totally on the current page. A new page should be created and only the text that doesn't fit should be moved to the new one.
A user inserts any other element (an image for instance), that doesn't fit in the current page. A new page should be created with that element.
I've tried to dive into the Google Docs JS code but it's pretty impossible to follow, since it's compressed. There is a standalone version of Google Docs, with the code beautified, however it's old and doesn't handle several pages.
Any hint about how to accomplish this will be appreciated.
If your container have a fixed size you can use the overflow event to detect it.
window.addEventListener ("overflow", yourFunction, false);
Basically you'd want two divs, like this
<div id='pageWrapper'>
<div id='page' style='max-height: 600px; overflow: hidden;'>
</div>
</div>
All #pageWrapper does is sit there and look like a page, all the content that someone adds is added to the #page. Everytime someone adds content, whether through pasting or typing check #page's scrollHeight versus it's offsetHeight. If the scrollHeight is bigger you've overflowed the page, and you can start moving content (word by word, or element by element) to the next page until the scrollHeight is equal to the offsetHeight again.
If the user inserts a page break, just set #page's height to wherever the page break is, so that anything that comes after that will overflow the page. This will get tricky with large documents, since if someone overflows page 1, content will have to be adjusted until page whatever, but I guess that's why Google Docs doesn't have pages.
I went through many posts and couldn't find a solution. (I came across with a similar problem in this post, but I can't really related to my exact situation).
Issue: I have to display a large data table (with more than 30 columns) on a screen. My challenge is - the client doesn't like Horizontal scroll bar of any mean.
I am thinking of splitting a table into pieces and using a simple sliders like Jquery: "serialScroll" or "ContentSlider". However, here is another challenge: for 508 compliance, my table needs to be a single table instead of splitting multiple table in different slides.
I am visioning I need a "view finder" div on top of "large table" with control to shift table the size of div Left and Right, http://img202.imageshack.us/i/tableviewfinderidea.jpg/.
My question is:
Is there a JS or Jquery solution out there doing this? (I seriously searched two weeks without any luck.)
Is there any other recommendation?
using a container div styled with 'overflow:hidden' allows you to do the masking as shown in your graphic. then it's easy to move the masked content by setting it to 'position:absolute' and tweaking the 'left' value.
i put a short example of this online at http://jsfiddle.net/BDZPj/
only thing is, you have to know the height of the content being scrolled, as you have to set a height for the mask too. you could read the height from the table on document.ready though, and apply it to the container initially.
anyway, like 'Pointy' says in the comment above, using a standard scrollbar is surely the best way to do it - sometimes the client has to move, too.