How to get element height without DOM? - javascript

I am building a SSR angular app and I got some logic which handles the location of different templates but in order to complete the logic I need the element height of each template without using the DOM.
The project, in short
I am building an app that styles HTML into looking like a PDF and then using that HTML to create a PDF using Aspose.
Why SSR?
SSR is needed for other platforms than a browser to get the HTML that then renders into a PDF. I.e, I have an Api that is able to call the SSR app, get the full HTML and print it into a PDF.
Why Angular?
The entire project is build on Angular and I need to be able to reuse the component that renders the PDF-look-alike HTML for direct editing within the HTML.
Now that you are up to speed, back to the problem at hand
The HTML consists of multiple templates which height changes according to the data added. In order to push sections down (or up) to a corresponding page I need the height of each template (after data is added) to know if the content exceeds the page height.
When navigating to the SSR app via a browser everything is rendered fine because I am able to make use of the DOM and query into each template to get the height of the element. But when accessing the app via an Api or Postman I get the HTML back but the logic that handles the section placements are broken because it doens't have the height - SO I need to get/calculate the height of each template WITHOUT the use of the DOM.
What I have found so far
I am pretty new at SSR but everything that manipulates the DOM or something that only a browser engine has doesn't seem to be a viable way - so #ViewChild, ngAfterViewInit, setTimeout and other DOM Api's or functionalities cannot be used in this case. I need to be able to prerender each template at the lifecycle step NgOnInit (at the latest).
Using libraries like Mustache or HandleBars renders the HTML fine from the data that I give it but I ONLY get the Html - no dimensions at all.
I have also tried to use createElement('div') after Mustache or HandleBars creates the element and add it into the newly created div. This adds the element fine to the div's childnodes but it doesn't calculate the height.
Maybe I am missing something using one of these libraries?
Is it even possible?
So far I am getting the impression that it is not possible to get an element dimensions from code without entering the DOM?
Headless Chrome?
Is running a Headless Chrome the only way to create a sort-of virtual DOM that I can manipulate from code?
Sorry for all the text and minimal amount of code but this question is more on how my approach should be and if I am heading in the wrong direction.
Thanks in advance!

The issue you describe is that the actual height of the DOM element is calculable only after that the element is inserted in the DOM.
For example, if you compile your Handlebars template and you insert in a new div element created with createElement('div'), the actual height will still be 0.
You will need to append your div to a current existing DOM element in order to calculate its height.
A workaround i often use is to load the component you want to know its height into the bottom of the page (or in a location the user is not currently watching), calculate its height and then eliminate it.
All the process is almost instantaneous and allows you to get the actual height of the element.

The fix for this was to go with Headless Chrome - at least in my situation.
Without a DOM you are not able to render the height and if you do not have a browser engine to render this for you, you'll need to spin up an engine that can - i.e Headless Chrome.
Using Puppeteer was the easiest way for me.

Related

Get HTML from Vuetify components without posting it on the dom

For my own project I would like to fetch HTML code from multiple Vuetify components. Not as a HTML string, but really as a DOM element that can immediately be appended to the body. I want this so that I can use these components in a non Vue environment.
Is there any way I can accomplish this?
I'm not entirely sure what your use case is for this. If you could elaborate on what you're trying to achieve, I could likely give a better answer.
I've had some similar requirement in the past, where I needed to access the entire HTML content of components/sub-components.
How I did this was by rendering the components in an iframe. From there, you can access and modify the mounted iframe DOM.
I can't find the source code on how I did this but perhaps concept/approach could serve as a starting point.
From a cursory internet search, I found this link https://jsfiddle.net/ohznser9/ with a JS Fiddle demonstrating a i-frame component that takes Vue components as a slot.
I'd imagine you could extend this component to add functionality to modify or extract elements from the DOM.
Perhaps you could load the iframe invisibly, grab the DOM elements from that and do what you wish with them

How to create pdf export of a part of the current page on the client side

I try to understand if I could use PhantomJS (or any similar js library) in my web application (in a javascript function called when "ExportToPdf button is hit) to create pdf export of a part of the user's current page, for example the contents of a div, but with its current styles.
And I need it to be made on client side.
I see over my searches that phantomJS is used as a tool (console app executable), but could I utilize it the way I need it?
Maybe in combination with jsPDF or some similar js library...
Or if you have any other solution to suggest... I already have tried jsPDF alone and it does not get any styles at all (all bootstrap class elements are missing). Also I tried html2canvas+jsPDF to get image of the desired div contents and put it on a pdf doc, but this does not seem to get all the height of the div (only the part that is viewable in browser gets exported)

Restart/reload Angular app

I'm working on a project with Yii2 and Angular. The structure of the code is as follows:
<html ng-app="myApp">
<head.../>
<body>
...
<div class="body-content"> MAIN CONTENT GOES HERE </div>
...
</body>
</html>
The page contains a header and a column on the left and a center area which is rendered inside the .body-content div. Now, as you can imagine, I have some buttons in there, some other angular widgets, etc..
Yii2 has a really cool feature called renderPartial that will re-render a view file without wrapping it again in the <head> and <body>. I use that to update the content of my main area, by calling that function, getting the response with jQuery and replacing the content.
Now, that causes all buttons that where binded with Angular to stop working (I'm guessing why). My question is: How can I make Angular re-run or re-bind all my (new) DOM elements to their actions?
You would have to use the manual bootstrap way (explained in https://docs.angularjs.org/guide/bootstrap) for angular but doing that would cause a memory leak over time as angular add listener on DOM that you destroy and is not aware of it's removal, so they stay, and so does for the controller / directives / binding and other features that are referenced by your code.
Is yii2 could be wrapped into an angular directive?
I am not sure if I am getting you right - you use a frontend framework AND a backend framework to control your frontend, the latter one deliveres new DOM content you inject into your HTML?
As far as I got it, Angular is best in handling everything while getting the data (be it from the backend in JSON or other format of your choice), NOT new DOM elements.
Angular itself binds to whatever DOM nodes it is added to, handles the content and dependency injection and thus displays your data. In your case the backend PHP framework seems to hand in new DOM elements that Angular thinks of "Hey - you don't want me to be adding them? fine, then I don't." and breaks.
Maybe there are solutions for this specific case, but if I were you I would rethink the concept in terms of "Where do I want my views/templates to be rendered? What part of the whole is responsible for what?" Using two different frameworks, let alone languages, to do the same job and interfering with one another would make a mess I wouldn't want to clean up.
So if you like this Yii2 feature and want to make it work, fine - but in that case - what do you need angular for? And if you'd rather stick to Angular you just have a backend that handles data and hands it back to the frontend to do it's job with it.

Pdf.js renders text inconsistently on canvas

I have a fairly large AngularJS app. I've integrated PDF.JS simpleviewer.html code from the examples folder inside my app(didn't modify it). When I use PDF.JS to render a single page within the container(not iframed), I get very inconsistent text rendering:
Original
Inconsistent boldness
Inconsistent fonts
When I use the same pdf file with the examples code outside of my app, it always renders correctly. It's only when I integrate simpleviewer.html inside my app (not modifying a single line), that this issue appears.
Can you suggest what might be the root cause for it. Which areas should I look for conflicts?
I figured that if I have multiple instances of the viewer on that same page, they will try to render pdfs all at the same time -> this causing the bad rendering of text. If there is just one instance, text renders fine.

Preventing CSS of Jquery Dialog content from being applied to main window

In my web application, I have written a cross-domain ajax call which is fetching an HTML page from a different domain. This newly fetched page is being rendered in a jQuery dialog using the following code $('#previewDialog').html(response).dialog('open');
This renders the response properly in the dialog. However, the response (HTML page) also has some CSS styles in it. These styles (generally BODY, INPUT etc) are getting applied to my main window (parent page) and distorting the complete view of the page.
When the dialog with the HTML page opens, the view of the parent page is completely distorted because of the CSS used in the HTML page (response of AJAX call) which gets applied to all the components. And when I close the Dialog, the parent page gets back into shape.
Is there anyway, by which I can prevent the CSS of the HTML page which is being displayed in dialog, not get applied to my parent page?
Trivial answer: have everything from the page that you pull in be wrapped in a div with a class not used elsewhere. modify the .css for that page so that it only applies to elements within a div of that class.
Edit: If you cannot control the css of the origin page, things become somewhat more complicated. your problem, though, is that you're injecting the HTML (including the css link) directly into your page. Instead, try the following:
Grab the HTML for the other page. Place it into a div off to the side that you're not using for anything else using the html() command.
Go into that div using the jquery DOM commands. Grab the portion of the page inside of the troublesome links, and pull it over to the $('#previewDialog') location. Destroy the contents of the working space div. If there is javascript or css that you need to preserve, have it entered (modified, if necessary - like with div wrappers) elsewhere in the page.
Now, this only works if the pages that you're being fed don't have their css or javascript changing with any frequency.
An alternate version of the same thing - while you have it as a response (a string format) use string manipulation tools to excise the css reference, rather than using DOM commands to pull what you need out of it.
More complicated/difficult version of the same thing (though somewhat more robust): Use string commands to slice out the css references (as with the alternate version) and then make another call using that css reference to acquire the .css file. Use string commands on the .css file to add in the div-wrapper limits as initially described, then insert it elsewhere on the page as an internal style sheet.

Categories