How to Make An Embeddable Widget?

I want to start a new HTML/CSS/JS project that allows the user to create a bar graph by completing some inputs (text, checkbox, etc). The app would then create a responsive bar graph. The graph would also include interactive elements like mouse hover effects.

I want the user to be able to export the graph as HTML. I’m thinking of something along the lines of social media embeds like Twitter. However, none of the data would come from a back end, since the user will input all the data for the graph entirely on this app.

Can I do this as a purely front-end project? If so, what techniques or libraries should I be looking at? I’ve come across Web Components, but I’m not sure if this is what I’m looking for.

Any help is appreciated.

I’d actually start with how you want the “export” to be used. Idk if Twitter supports “HTML graphs” at least in the sense of “exporting it”. As there isn’t any way that I know of to upload raw HTML to twitter to get it to display. Usually its done via a screenshot, or some other form of an embed that twitter can get via a URL.

I’d start here, as regardless of how you build this project, you must build it in a way for the “embed” to work how you intend, otherwise it wont work how you want it. Questions like “can this be front-end only” and “could I use web-components” all boil down to where you want the embed to be used.

1 Like

Yes you can do this. But it’s going to be a little bit of a pain.

If you wanted to just allow them to d/l an image then you would have your inputs, and you would translate those to a drawing in a <canvas> element. You can then grab the output of the canvas as an image, and the user can download that.

To get markup instead you’d want to use SVG, and issue is that it’s a bit of a chicken-egg problem – the thing you want the user to be able to d/l is the thing the browser uses as instructions to render images. So you need to be able to produce both the markup on the page that shows the graph and a text version of that exact same markup. And those have to be generated in lockstep. If you had the text version in say a <textarea> in a form, you can then allow the user to essentially submit the form and the result of that will contain your markup.

To get markup instead: you again have your inputs, and you take those and apply them to SVG (which is actually going to be a bit simpler than canvas for this IMO). You probably have a set of elements set up that you use (if user wants a bar chart, use these elements, if a pie, these elements, etc). And when they pick them and set the values, you adjust the text version as well. you provide a d/l button that grabs the markup from the page via the DOM API, turns it into a blob, then let’s the user get it.

These are the ways I’d approach it, and there are probably a few other methods as well, but you’ll want to look at:

  • canvas API (should you go slightly easier route of just allowing image export)
  • the Image, URL and Blob APIs
  • SVG (should you definitely want markup)
  • form-related APIs should you want to go down that road
  • general DOM manipulation APIs (adjusting attributes, event handling etc)

Web Components are a way to make new elements with custom behaviours – they are good and useful but they don’t really apply here unless you wanted to create a special “graph creator” component or whatever. It wouldn’t change how all the stuff above worked, it would just be scoped into one single element (or collection of linked elements). But that isn’t important here, WCs don’t provide anything particularly useful for your usecase.

(Edited re getting markup)

2 Likes

Thanks for the suggestions.

When I was referring to Twitter embeds, it’s actually a <blockquote> element followed by a <script> element. Here’s an example:

<blockquote class="twitter-tweet">
    <p lang="en" dir="ltr">The Udemy Struggle is real y&#39;all! As a software dev I am always buying stuff on there 😭😂 <a href="https://t.co/zWb8M74Xk7">pic.twitter.com/zWb8M74Xk7</a></p>&mdash; Danny Thompson (@DThompsonDev) 
    <a href="https://twitter.com/DThompsonDev/status/1608155351932887046?ref_src=twsrc%5Etfw">December 28, 2022</a>
</blockquote> 
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

So presumably the script is immense (it is, I checked) and probably does stuff like load styles, query the back end for data, and a bunch of other stuff.

But what if I’m not interested in any back end data? What if I want the user to be able to create a graph in the app, which I can display in a <div> or a <canvas>, can’t I export that element the same way?

Sorry if I’m not understanding an answer that’s already been presented. I’d prefer to have actual markup that the user can interact with, rather than an image. I figure the latter is much easier.

Note that the HTML element is not really relevant here: the <script> is mandatory because it needs JavaScript, but beyond that it could be literally any combination of elements.

What do you mean “can be interacted with”. What do you expect that they are going to do with the graph once they have it? Why do they need to interact with it? Would they not just look at it?

Anyway, this is almost valid code but note it won’t work as copy paste afaics, just as an example of how you do it (in this case produces an svg download)

<!-- ...some HTML file -->

<svg id="downloadableGraph">
  <!-- SVG stuff here -->
</svg>
<a id="downloadDownloadableGraph" download>Download graph</a>

<script>
const graphMarkup = downloadableGraph.outerHtml;
const blob = new Blob([graphMarkup], { type: "text/svg" });
const blobUrl = URL.createObjectUrl(blob);
downloadDownloadableGraph.href = blobUrl;
</script>

As described, things you want to look at are DOM API, Blob API (of which File API is a specialised extension, I’ve just used Blob directly though), Url API. Doesn’t need much more code than above for fully functional version (for example, want to only make the anchor visible once there’s a URL available. You want to revoke the URL as soon as you can. If you do use SVG, needs to be a valid SVG document so need some extra XML in place to conform to spec, etc etc)

2 Likes

Ok thanks. I’ll look into those APIs and see what the possibilities are.

Also, I kinda ignored the title of you post. If you actually want an embeddable widget, Web Components do give you that. They don’t have the friendliest API, and personally I maybe would get something working then convert it to a WC, but would be fine to create one up front. Probably makes it a bit easier handling the state, possibly? Anyway, can just be JS, and works in basically all browsers.

So, ignoring the logic for graph creation, just to make clear (I meant to comment the code):

  • I have some markup of some kind created for the graph
  • I have a link to download it. NOTE the download attribute: this indicates to the browser that this is probably not a link to an external webpage, that it is a resource the user wants to download. So the correct browser/system dialogues should pop up.
  • I populate the href of that link with a URL that is an encoded version of the markup I want the user to have

So

  • I select the root element of that markup with JS.
  • the result of selecting it is a DOM object.
  • that has a property called outerHtml, which is a string version of all the HTML
  • I take that string and construct a Blob to hold it.
  • a Blob is an object of raw data, very much like a file.
  • the Blob constructor’s first argument is an array of stuff that will make up the blob: I pass it the string I got from outerHtml
  • second argument is options, and I can specify the MIME type there (could be “text/html” for example).
  • next thing I do is create a URL from that object.
  • I use the createObjectURL method. This takes an object (the Blob) and creates a string from it you can use as a URL.
  • as an aside this can be expensive (think if you had a huge image for example), so the advice is that you revoke (eg destroy) it asap, as soon as it’s not needed.
  • I can then take that URL and set it as the anchor element’s href attribute.
  • Now, when the user clicks the link, it should pop a dialogue up asking if they want to save that blob. With it having a MIME type, it should recognise that, and it’s going to ask to save the object as a file of the type you want.

That’s basically all you need – like four or five lines of code (if you ignore things like catching errors and stuff) to get something you can see working immediately – you can iterate from there.

Then you have the logic to create the graphs – that’s a whole nother thing, you’ll need quite a lot of fiddling around there!

Finally, I’d strongly advise SVG for (smallish) static graphs, cos you’re going to want graphics primitives (lines, curves, rectangles, circles, etc etc), and the end result is markup and is also an image. This is a massive faff once you get into adding legends and pips in the right place and labels etc etc, again going to want to do some fiddling around.

(Edit: with the SVG + the inputs for the graph data this is where starting with a web component may help: you’re going to be starting from a position where you have a class, with internal state, so it forces a bit [heavy emphasis on ‘a bit’!] more structure immediately – not as simple as React et al, but a bit more logical than just raw DOM stuff)

1 Like

Thanks Dan. You’ve given me a lot to think about.

I’m starting this project now to build on things I’ve learned recently. I don’t think this’ll be a quick project, but I’ll report back once I’ve got an MVP.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.