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
Related
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.
While docusaurus offers full control over the content/structure of the document (pages/docs), this control is embedded inside an uncontrolled (DOM) parent.
If for example I want to have different padding to the entire page but only for the index page, I have no way to do so other than change the value using javascript.
Isn't there a way to somehow insert the document id into the DOM, something like:
<body id="document_id">
Or class= instead?
Or, is there any other way for me to write specific css rules or js logic for specific pages?
Docusaurus maintainer here! Sorry for the late reply. For the root of index pages, there's the homeContainer class by default. Could you add your padding there?
I am writing a chat widget, that would be distributed to the end users with little code to put in their website. Usual routine.
My widget is going to be written in React. I know several ways to achieve this. Let me list the ways that I could think of.
Give a code snippet with directly iframe and source url in it. Problem with this approach is, it can be used only if the widget is embed. If the widget needs to be popup, flexibility will be lost.
Give a code snippet with a javascript being loaded asynchronously. Javascript will create an iframe in the parent webpage and src can be set. This widget javascript can have little intelligence. This is the usual approach followed by most of the widget developers.
Of course, source URL will render a React page which is bundled by webpack, in either case.
I wanted to know the best practices of developing a widget. So I went through the popular implementations of it. I liked Intercom's widget very much. It is written in React. I analyzed how it works.
The minimal javascript is loaded async on the webpage. It is injecting an iframe with id intercom-frame. That iframe has a script in it's head with a source URl. Obviously it is React bundle.
The thing that I don't understand is, below this iframe, a div is created with three iframes in it. One to show the chat bubble, another to show the chat bubble icon, the last one to show the actual chat window. Those iframe doesn't have source url and I guess the bundle is served from the first iframe created by the widget javascript.
I came across this SO question, which partially answers my question. From the answer,
expose some API between your customer webpage and your iframe, using window messaging.
the main code (the iframe code) is then loaded by this first script
asynchronously, and not included in it.
What I don't understand is,
1.) How they would have achieved it with window messaging?
2.) How they would have managed to create a div with iframes in it, from another iframes script?. Widget javascript is not creating those elements, based on it's source. It should have been done by the React bundle in the iframe generated by widget js.
3.) How a react bundle inside an iframe can create react elements in the parent DOM?
None of the iframe created by Intercom's script has src attribute, that means they are not subject to the same origin policy. Therefore, they can modify parent page html and vice versa.
However, I don't understand why they need to have separate iframe. And why using a script to inject another script which inject the main html content. Doesn't the first script have enough ability to inject html content? I'd love to be lightened about these things.
I've an Angular 2 component, which contains multiple sub-components. For a few of them it's quite expensive to load them and sometimes it is not necessary to load them at all. For example if the user is not scrolling that far.
Anyway, I know how I can lazy load routes, but is there a way of lazy loading a template? Like only if a element is in or close to the Viewport?
There is no way to lazy load templates. What you can do is to lazy load modules. How to manually lazy load a module?
If you use this with ViewContainerRef.createComponent() (see Angular 2 dynamic tabs with user-click chosen components for an example) to dynamically add the components that you only want to show if the users scrolls far enough, it might work (not tried myself yet).
You can segregate or group sub-components to be displayed into smaller components to be loaded together.
To reduce the time to load,
1. try to use smaller templates inline into component file.
2. Use *ngIf directive in your template which can avoid rendering of the template and component instance is not created as such. However, take note that if you're using *ngIf it is better to use only is the DOM is not refreshed fequently, else you may create DOM and use the component by binding it with [hidden] attribute of the DOM
I want to unify the navigation layout for my website, so I created a separate html file that holds the code for the navigation. I use a JS to load the file dynamically:
$("#navigation").load("/navigation/navigation.html", function() {
$.getScript('/material.min.js');
});
The problem is that the material.min.js does not get executed for the dynamically loaded components inside this html and I lose some crucial functionality. How do I fix that?
Check if the componentHandler is loaded, and try to upgrade the elements after load.
if(!(typeof(componentHandler) == 'undefined')){
componentHandler.upgradeAllRegistered();
}
How the Component Handler works
In short this goes over all registered components. Queries for all nodes with the provided CSS class. Loops over those and instantiates them one-by-one. When the upgrade is done on a node, the upgraded object is added to the dataset. This object contains a comma separated list of component classAsString properties to identify which upgrades have been done.
From the official docs:
Material Design Lite will automatically register and render all elements marked with MDL classes upon page load. However in the case where you are creating DOM elements dynamically you need to register new elements using the upgradeElement function.
So loading the material.js script again will not execute it. But you can do some experiments with upgradeElements by applying it to the whole loaded markup or to particular elements.