Could you provide some specific example of something that can be easily done with custom elements that cannot be easily done with other existing APIs? (I'm speaking about user-facing stuff, not some implementation detail.)
(1) It doesn't let you have multiple progress bars that all advance at their own rate.
(2) It doesn't work at all for custom-progress-bar elements added dynamically to the document after the timers for the first progress bar finish.
(3) It makes the children of <custom-progress-bar> actually appear in the DOM, which means that code generically walking the DOM will see them and might mess them up.
The basic thing that the combo of Shadow DOM + Custom Elements provides is encapsulation. You can make standalone elements that operate themselves, work just like normal elements, handle attributes and children, and start by themselves. Anything done using encapsulation can be done without encapsulation, in a sense. What encapsulation provides is better organization for your program, and more elegant and understandable code at the point of use.
> (1) It doesn't let you have multiple progress bars that all advance at their own rate.
It does.
> (2) It doesn't work at all for custom-progress-bar elements added dynamically to the document after the timers for the first progress bar finish.
It does.
I think you're confusing the "library" code with the "user-land" code that drives it (the latter was copied from the example in the original article). Here is a version with two progress bars where I separated the "library" into a separate box:
(3) It makes the children of <custom-progress-bar> actually appear in the DOM, which means that code generically walking the DOM will see them and might mess them up.
This part is true. However, if you have code that generically walks the DOM and screws stuff up, how do you expect your website to operate anyway? The real issue is overly broad CSS selectors, and it's not really something specific to custom elements. Everyone has to deal with it for all CSS and all elements. In this case, it's actually easy to mitigate. Since all the inner elements are custom, I can easily add a prefix that will effectively prevent them from being styled by other libraries. That said, I wish W3C actually solved CSS scoping properly. That would be way, way more useful than any component API and it would be useful for every website in that uses styling.
You've solved one set of problems (and fixed the major problem that the original had no separation between library and user land code) and created some new problems:
(1) A timer will fire forever, even if no progress bars are currently updating. Bye bye, battery life!
(2) A progress bar can't update at 60fps, even if changes to the underlying value happen that quickly.
IF you continue playing whack-a-mole with these types of problems, you might see why Custom Elements + Shadow DOM are an elegant way to avoid these issues while providing good encapsulation.
(As someone else mentioned, Shadow DOM provides style scoping so it doesn't have the CSS issue you raised, even if the contents are normal built-in elements, while your example does.)
I hope this helps to clarify what Web Components technologies are good for.
>You've solved one set of problems (and fixed the major problem that the original had no separation between library and user land code)
My original followed the structure outlined in the article's example. It runs the exact same library code as the second link. I did not "fix" anything, since nothing was broken in the first place. I simply split it for clarity.
> A timer will fire forever, even if no progress bars are currently updating. Bye bye, battery life!
You claim regarding battery life is unsubstantiated. Simple timers like that take up very little processor time as long as you don't do superfluous DOM updates in every invocation.
> A progress bar can't update at 60fps, even if changes to the underlying value happen that quickly.
Firstly, it can. If you set the timer to 16ms and there is nothing else loading up the browser engine.
Secondly, if you think that your typical events always updates the page graphics at 60FPS, you are mistaken.
But most importantly, you really don't want to update a control like this 60 times per second, because you would be wasting CPU cycles on re-calculating values that no human will ever notice.
> You claim regarding battery life is unsubstantiated. Simple timers like that take up very little processor time as long as you don't do superfluous DOM updates in every invocation.
That seems intuitively plausible, but for modern CPUs, it is very wrong. To give a specific example: modern Intel CPUs have a wide range of power levels including idle states called C-states. C-states are very low power, but somewhat expensive to enter and leave; while in a C-state, a CPU core can't really do anything besides wake up to a higher state. A frequent timer, even if it does almost nothing, stops the CPU from being idle long enough to stay in a C state profitably. Thus, frequent timers that do almost nothing can hurt your battery life more than sharp but very infrequent spikes in CPU usage.
My claim to expertise: I was heavily involved in optimizing Safari and WebKit for battery life. Most of what we had to do was not reducing average CPU usage, but rather preventing gratuitous wake from idle, or where not possible, coalescing repeated wakeups so they happened at exactly the same time instead of slightly offset. We actually measured this and it is a major effect. In fact, tuning this way is one of the major reasons that browsing in Safari delivers longer battery life than browsing with Chrome on the same hardware. I am going to bet you did not measure and your claim is based on speculation.
On mobile devices it's somewhat different. The screen and radio are a much bigger portion of the total power envelope so the CPU doesn't matter quite as much. But as mobile CPUs continue to get more powerful, maintaining good idle hygiene will become more important there too.
Short version, and something all web developers (and native developers should know) should know: idle timers are extremely costly in terms of battery life, when your app or webpage seems looks idle, it should actually be idle!
> Secondly, if you think that your typical events always updates the page graphics at 60FPS, you are mistaken.
> But most importantly, you really don't want to update a control like this 60 times per second, because you would be wasting CPU cycles on re-calculating values that no human will ever notice.
When the process driving the progress bar is making material progress, it is both feasible and desirable to update the progress bar within one frame.
Since I don't know what exactly you measured, how you did it, on which devices, and for what use cases, I cannot comment on specifics of what you said.
I tested various variations of this approach for extreme cases (large pages, many timers, complex CSS queries). The visual performance and CPU utilization were much, much better than I initially expected. My testing for mobile was less thorough, but I didn't notice anything horrible going on with the battery, precisely because of what you said: the browser-based drain was negligible compared to drain by screen, network and background services. I admit neglecting to test this on unplugged laptops. I will eventually get to it, but I don't think the results will be as bad as you make them sound.
Regardless, notice how the conversation shifted from "this is impossible" to "this doesn't scale" to "this might be not very battery efficient in some cases". I consider it premature to dismiss this approach without knowing exact costs for battery use in real-life browsing scenarios. (After all, I'm not the only one on the web using timers, and some other things many websites do today slaughter both battery and visual performance, and people continue using them without thinking twice.)
The approach has merit. In fact, it has a lot of merits that aren't obvious from such a simple example. The true benefit of timers is that they illiminate many issues related to events and inter-component interaction, thus drastically cutting down on code complexity in more dynamic, more complex pages. Custom elements are not going to address that any more than ASP components did in their heyday.
So you have to run a querySelectorAll() across the whole document every time you want to update/upgrade your elements? That's not going to scale at all.
You should actually try it before making such statements. It scales well enough. Also, we could use getElementsByTagName instead of querySelectorAll. But that's not important.
The point is that there are approaches to handling "components" with current APIs. They are simple, they work across all browsers right now and they are good enough for 99% of websites out there.
I'm using it to build wrappers around d3-based graphs to make it easy for non-software engineers (mechanical, electrical), to create and modify simple PID controls. There's PLC programs and interfaces but by and large they're klunky, aren't customizable, or require learning something g like LabView.
The custom elements help specifically by encapsulating the (somewhat tedious) d3 graphing and controls connections into simple HTML objects with a simple spec the engineers can lookup. HTML is then declarative and easy for non-programmers to try, test and see immediate feedback. This works out well for creating functional controls systems which can then be cleaned up by experienced developers who can tune the underlying web components.
JavaScript libraries all require much more knowledge and generally don't "scale" when you want to add several graphs quickly. Frameworks like Angular2, Vie, or React all require significant learning curve and understanding of the framework generally to get working. Though Vue seems to be the simplest, but web components provide an API which Vue et al can build on by encapsulating more complex HTML/CSS/JS into semi-hidden sub-trees.
Edit:
Here is my quick re-implementation of their example using today's APIs: https://jsfiddle.net/pd0na425/3/
The HTML instantiation uses a single tag, just like theirs. Fully reusable. Works in all modern browsers. I believe is uses less code too.