Getting tired of all the EVO/Android/iPhone love Jake’s been spewing lately? How about something a bit more dry and developery? As promised in a prior post, here’s a more technical writeup of how I built the WebCenter sharing bookmarklet. I was hesitant to write about this because it’s not like writing a bookmarklet is new and interesting… there are tons of tutorials on the web after all. But as with everything, writing about it will make me appreciate the work I put into it more, I suppose.
There are a few pieces to that make this bookmarklet possible:
- Content iframe originating from the domain you’re sharing to (bookmarklet.html, #3 in the diagram above) — this is necessary because our bookmarklet needs to interact with a REST API on the domain where the user will be sharing to (in this case, the host/domain where WebCenter).
- Proxy iframe (#4 in the diagram above) — this iframe is hidden off of the screen and is used simply to trigger events that happen on the parent. I’ll go into more detail as to why this is needed later.
Here’s the flow:
- Inserts a stylesheet to the page defining the style of the dialog box we’re going to create (#2).
- Defines a div container for us to insert our DOM elements into which makes it easier for cleanup when after the bookmarklet is used.
- Inserts the content iframe (#3) which points to bookmarklet.html on the WebCenter host.
- Inserts the proxy iframe (#4) which points to proxy_frame.html on the WebCenter host.
- At this point, the sharing dialog window has been initialized and the iframes have been rendered. Our bookmarklet communicates with the WebCenter REST API directly (#3). Before presenting the user with the “publisher” (sharing textarea), we need to determine if the user can access the REST API. Since our bookmarklet app is hosted on the same domain as the WebCenter app, we don’t can trigger the SSO system to authenticate the user if it’s needed (no fancy OAuth needed — good thing because WebCenter doesn’t support OAuth yet). Upon a successful SSO authentication, the iframe in #3 is reloaded and the REST API is called in order to paint the rest of the form.
- The only interaction left is when the user clicks the “Share” button. The Share button is purposefully inserted on the host page (not the iframe in #3). The reason for this is the share button will need to be able to trigger the dialog to close and also clean up all of the HTML that was inserted onto the page. You can’t do that if the button is inside the #3 iframe. However, this causes us another issue. How do we trigger the form submission in iFrame #3? Because of cross domain rules, you can’t trigger an event in another frame that’s from a separate domain. Enter iFrame #4, the proxy_frame.html. Before I explain how this works, go read Michael Mahemoff‘s excellent article about Cross-Domain Communication with iFrames — the technique I use is the “Window Size Monitoring” hack. The hack works like this…
- The main page (i.e., main parent) creates two iframes (#3 and #4), both of which reside on the same domain, but not necessarily the same domain as its parent.
- The first iframe contains the actual iframe you want the user to interact with (#3). The second iframe is the “proxy” iframe (#4). The proxy iframe’s purpose is to respond to events that the main parent triggers.
So, that’s the gist of it. Questions… ask below.
Thanks for the post. Out of curiosity, how are you managing the closing of the bookmarklet. I have two external buttons adjacent to the content iframe: save and close. Close is easy because it can just remove the bookmarklet container div from the dom and nothing else needs to happen. But If I want to go ahead and process the action, which in this case is a form post within the content iframe, I have to trigger the resize of the proxy which sends the action to the content iFrame, and then still follow up and remove the container. If I immediately call remove() on the container, the iframe action never happens because it was closed too fast.
Functionally, when we added the ability to post without the link of the page you're current on, we also had to add a confirmation message with a close function. Initially, once you clicked Share, the bookmarklet window disappeared on its own.
So, I guess the guys can answer your question, even though we went away from that approach.
Hoping Rich will see this and add relevant technical details.
We opted for the first option you mentioned. Because our bookmarklet can be injected to any site on any domain, there's no way to set the proxy on the parent. So, we've opted for the simple “close” button approach on the parent and detached any events from the submit button… this forces the user to “close” the bookmarklet after they've submitted. This seems to be the popular choice.
Thanks for the replies. Ideally, I'd like to have the bookmarklet window close after the action has completed, without requiring a second click after the action has completed. I've seen it done but haven't got a handle on how yet. Cheers.
The way to do that is to put your submit button on the parent. When the submit button is pressed, send a message to the child frame to submit the content (through the proxy), then you can close the window after the callback has returned.