Taming the UX monster with SVG: my first encounter with custom SVG connectors and how you can excel it at too!
- I needed to make an interaction like above where user can select a node on LHS and connect with nodes on RHS.
- There can be many-to-many relationship between these nodes.
- User can chose to remove an existing connection.
- All existing connections have to be plotted when page loads and auto-saved whenever user adds/remove a connection
- The cherry on top was - filter! User can filter the nodes on LHS or RHS at anytime and the Connections have to show according to currently visible nodes
- After sweating my fingers for few days and losing some hairs, the final outcome was like this:
Choosing the right weapon
Luckily, I had some prior experience of plotting complex relationships between datapoints using D3.js as shown in below diagrams
The output looks very similar to what I needed, so I started working on achieving the requirements using D3 js. As the project was in VueJs, I also started looking for VueJs + D3Js implementations and VueJs plugins to show Tree/Relationship/Organogram etc.
There are many such libraries/plugins in D3 and Vue with nice output, but they all were limited to showing existing connections between simple Text nodes. In nutshell, they took a json object with list of nodes and their relationship with corresponding nodes. They had very limited support for Rich markup/html and user-generated events.
I needed to support inhouse component library and also quick filter on each side of nodes, to change both markup and connections between them at user’s will. Also I needed events to save the latest connection when it was added/removed by a user.
So I concluded that rather than extending an existing library and still be limited by its original design, its better to make a custom solution, solving the project-specific need and it would also be easy to scale up or modify it considering that it would not be a complex third party library.
As all these libraries use mainly SVG, I started exploring SVG and alternatives. I found following:
- CSS3 + HTML + JS: while it may have been possible to make a solution simply using CSS and complex borders/boxes, it would have been very difficult to manipulate the styles using plenty of JS code. And hence I gave up after spending couple of hours.
- Canvas: this could be an ultimate solution considering the powers of Canvas, but I had little exposure to it. After bit of study I built an opinion that Canvas is ultimate super-set of all interactions HTML has to offer. But it may be an overkill considering my minimal Drawing requirements, while canvas offers vast Painting solutions at cost of verbose API.
- SVG: They are easy to learn, offers inbuilt api to draw simple lines and even really complex curves. You can create many types of nodes inside an svg element and add/remove them easily based on user events. Has lightweight api and easy on browser performance. This looked like perfect weapon!
Know thy Weapon
This blog is not a guide to SVG, so i will restrict myself to highlight only the essential parts that i picked up from my study of svg
- Its as easy as following to write your first SVG path.
- You need an SVG element, with various elements like Line, Path, Archs etc inside it.
- You must know the start and end x, y positions of the path you want to Draw.
- The api is simple to start with but can really mess up your head after a while! So try to keep it small and manageable.
- I ended up writing following (crux of) code to support various variables to draw a curved connector path
Knowing SVG api alone wont solve the problem as it needs start and end values of X and Y coordinates. For this i had to learn following apis which are rarely used in normal web app development.
They are really interesting to know and play around with, and it will help you realize the amazing relationship between browser Window and user’s Viewport!
The development was full of surprises as i learnt that
- Simple Web APIs like childNode.remove() to self-remove an element from DOM was not supported by our long-term frenemy IE 11
- As of writing this article in early 2021, its not possible to give Z-index to an SVG element, so if you want to plot an Active or high-priority element on newer element, wait till this gets accepted!
- Element.offsetParent.getBoundingClientRect(): had to use PARENT container as the Checkbox will always give offsetTop = 0 as its hidden due to custom styles we used to show fancy checkboxes
- While its possible to use Gradients to fade out a path, the gradient becomes invisible if the delta between Y coordinates is 0! And that needs special instruction as below.
- Its possible to create really complex or pixel-perfect paths in SVG, but that would lead to some utter complex equations to be solved like below. So i would suggest to simply refrain from it and look for alternate UX rather than building a nightmare for yourself and future devs.
Keep the map handy
The last thing i want to share with you that the journey was nothing short of a roller coaster ride, and hence it was really useful to make a map/flowchart of the complex user and page events and make the functions reusable and avoid duplication of code.
This will also help you understand your own code in future and write proper unit tests covering various scenarios.
Have a great time developing fantastic UX and feel free to reach me if i can offer any assistance in a similar ride!