DOM Events (DOMContentLoaded) in a web component - web-component

I've created a native Web Component (HTMLElement) and I am using it to expose an otherwise existing component (lowercase 'c') that is a fragment comprised of HTML/CSS/JS ... basically wrapping it in Web Component to help with its re-use and isolate it from the rest of a Web page.
The problem is the component i'm including in my Web Component has some JavaScript to lazy load images, and it lists for the DOMContentLoaded document event to trigger some of its JS which manipulates the DOM (injects the proper <img> tag) ..
I am able to get everything into my Web Component, and execute the component's JS within the shadowRoot (with mode: open) however the component's event listeners on DOMContentLoaded are never invoked.
I tried writing everything in my Web Component in both the constructor() as well as connectedCallback() but they acted the same.
Does a Web Component/ShadowDOM have its own DOMContentLoaded event? And/or does it respect the parent DOM's "state"?
If I execute JS in the context of the Web Component that binds an event listener for document... does that event listener apply only to the ShadowDOM? Or is that actually being bound to parent DOM?
UPDATE: I am loading my Web Component asychronously - so it's loaded after the parent's natural DOMContentLoaded event triggers.

No, there is no DOMContentLoaded Event for the Web Component.
connectedCallback triggers when the Web Component is injected(connected) into the DOM.
Yes, you can bind Events anywhere in the DOM and have access to everything in the DOM
(when using shadowDOM, that is one of the main differences with IFrames.. which can't access its "container")
Notes:
By your description, it sounds like DOMContentLoaded has passed; thus I presume you are defining Web Component late (are you loading scripts async?)
IF you create/load your Web Component ASAP synchronously (or define as a script in the head)
you can catch the DOMContentLoaded Event
But in general you shouldn't count on that behaviour as the user/developer is/should be in control on when scripts/components load.
The connectedCallback triggers on the opening tag of your Web Component. If the Web Component was defined early The DOM after the opening tag will not have been parsed yet.
Many libraries like Lit and Hybrids and Stencil provide methods like onUpdated that "help" or "prevent" you making mistakes ... you are learning a Tool, not the Technology.
In vanilla JS you have to create that behaviour yourself; easily done by adding a 0ms setTimeOut inside your connectedCallback. That effectifly waits till the EventLoop is empty and triggers when all DOM is parsed.
FireFox fired connectedCallback late for a long time, they fixed it March 2021
Long explanation: wait for Element Upgrade in connectedCallback: FireFox and Chromium differences

Related

wait until the page is loaded

In Robot Framework, does any keyword in any library implement this? Open Browser and Go To just go to the requested URL but don't wait until the page document is fully loaded.
Have a look at this discussion. You don't really need to wait for anything apart from Ajax.
In case you are using Angular, have a look at extendedselenium2library that implements waiting for Angular actions with every keyword.
If you are not using Angular, but you know which Ajax action you expect will fire before you can proceed with your test, have a Wait Until Page Contains Element or Wait Until Element Is Visible keyword with a selector for an element that is specific for your ajax request.

Webforms asp:Panel children not showing fully in VS 2012

I'm wrestling with design time rendering of my custom WebForms components where I have defined a control derived from Panel and a second control derived from WebControl.
I place the WebControl inside the Panel by dragging and dropping from the Toolbox. The problem I have is that at design time when I drag and drop the component within the Panel, the Paint method of the WebControl's designer is being executed immediately but the image generated by the interpretation of the getDesignTimeHtml and the application of the CSS rules is being executed a varying number of seconds later, typically between 1 and 4 seconds.
So my component is being drawn twice by two different pieces of logic. How can I synchronize these events so they both are executed at the same time? Having to wait for a ghost image to clean itself up is distracting for the developer. Note if I place the WebControl outside the Panel both methods are called at the same time, and they are called immediately.
One solution is to invoke the Invalidate() method of the PanelContainerDesigner that belongs to the parent System.Web.UI.WebControls.Panel. Invoke this from the OnPaint() method of the child component's Designer.
To prevent needless looping, let the child component's Designer remember and verify its position data on each OnPaint() call, and only invoke Invalidate() on the PanelContainerDesigner if there is a real change.

How to get a DOM elements click handler using capybara-webkit or native QtWebKit?

I'm using capybara-webkit and would like to get the event handler(s) bound to the click event of an element in the DOM. An answer even using native QtWebKit calls would probably be enough for me to figure out how to do it using the webkit driver in Ruby. The challenge I am having is that the event handlers are being bound programmatically in JavaScript, not in HTML, and my searches so far on how to do this all seem to end with how to click or otherwise trigger events in a QWebView. I need to inspect the event handler (i.e. the actual function definition), in particular anonymous functions bound to the event, without generating the event itself. Any help is appreciated.
You can execute arbitrary Javascript and return the result using page.execute_script. If the event handlers in question are bound using jQuery, this will list them:
page.execute_script(<<-JAVASCRIPT)
var handlers = $('.some-selector').data("events").click;
jQuery.each(handlers, function(key, handler) {
console.log(handler);
});
JAVASCRIPT
There's a complete answer on introspecting on event handlers here: How to find event listeners on a DOM node when debugging or from the JavaScript code?

Why do subcontrols initialize before their containers?

Despite working with WebForms for years I still find myself getting confused about the event lifecycle from time to time. This isn't so much a problem that needs resolving, as hoping to get a better understanding of why things work the way they do.
Suppose you have a form:
Default.aspx:
<form>
<MyControls:UserControl1 runat="server">
</form>
UserControl1:ascx:
<MyControls:UserControl2 runat="server">
The OnInit events occur in this order:
UserControl2_OnInit
UserControl1_OnInit
Default_OnInit
Isn't this just bass-ackwards? Shouldn't the Init code be run in the order that controls are created? Shouldn't a parent control be able to initialize properties of a child before its OnInit runs? That is, while you can initialize properties of subcontrols in markup, there's no direct way to have a parent control be able to dynamically set properties of the child control that will be available to its OnInit event.
What I've ended up doing is stuff like this:
override void UserControl2_OnInit()
{
NamingContainer.OnInit += new EvenHandler(UserControl1_ActualInit);
}
protected void UserControl2_ActualInit(..) {
// do actual init code here, which will occur before OnLoad but after it's parent
// OnInit
}
So it's not an insurmountable problem. I just don't understand why it's a problem in the first place.
I realize that perhaps you might want to be able to have all your child controls initialized in your OnInit code. So fine, you should be able to call base.OnInit first, instead of after, your own initialization code, which should cause all the child control OnInit events to get run. But the event lifecycle doesn't work that way. The Init events are not chained recursively, they seem to run independently the parent events, and the innermost one always gets run first. But seems life would be a lot easier if they were simply chained recursively so you could either call the base event (or not) before you do your thing in any given situation. Is there something I'm missing that makes this seemingly counterintuitive situation desirable or even necessary?
This document should be the main source of truth for your lifecycle questions.
Basically, OnInit fires after a control's internal initialization in finished. Since the page control is the first control initialized, and during it's internal initialization it initializes all sub-controls (perhaps in the order that the Designer.cs file gives), then it makes sense for the Page's OnInit event to be the last one called, since it's not finished initializing until all it's sub-controls are initialized and their OnInit events fired. Graphically, it looks like this:
Page internal init
Sub-control1 internal init
Sub-sub-control3 internal init
Sub-sub-control3 init finished / OnInit fired
Sub-control1 init finished / OnInit fired
Sub-control2 internal init
Sub-control2 init finished / OnInit fired
Page init finished / OnInit fired
So order of inits in this case is:
Sub-sub-control3 OnInit
Sub-control1 OnInit
Sub-control2 OnInit
Page OnInit
Load also work similarly. In general you should treat most of the events as though the control will go through it's internal process first (which includes calling the same event on all sub-controls), and then fire your custom event handling code afterwards.
Most examples you find use Page_Load specifically because that should be the last event called in that phase (and it's after post back data is loaded). It wouldn't work very well for Page_Load to be called first and risk having controls not in a fully loaded state for your custom event handling code.
The mindset for asp.net parent & child controls is:
Parents know all about their children, but children know nothing about their parent.
This mindset makes sense for re-usable server controls. Re-usability needs the custom child control making no assumptions about the page it gets used on.
The snippet you give makes me guess that your child user controls are not aimed at re-usable as such; but rather are specialized controls which you use to break down the complexities of a large & tricky UI?
In this case I would still try to work with the 'children known nothing about their parent' mindset. Think http://www.google.co.uk/search?q=gof+mediator+pattern where the parent page is the mediator between your children (the wikipedia page is good).
But your children still need to know something about the parent right, because they are doing complex UI interactions? You can address this with interfaces. Each child depends not on the parent, but on an interface that defines exactly what the children need access to. As http://en.wikipedia.org/wiki/SOLID puts it, 'depend on abstractions, not on concretions'. DO one interface per child control: 'many client specific interfaces are better than one general purpose interface'
But it all ends up looking over-engineered, doesn't it? It turns out that a componentised UI where the components must interact, just is complex, and the components may turn out big n clunky. This was, imho, one of the reason for MS web forms ajax controls losing out to jQuery &c. even before MVC came along.
Add to this that web forms ui is very hard to unit test; and your confidence in your software quality dives.
I recommend:
If you can, escape to a rewrite in MVC.
If you can't, consider abandoning server-side controls which do clientside behaviour, and use jQuery instead.
If you can't do that, simplify simplify simplify the UI. Even if that makes it less functional.
If you don't want that, then your choices are: pay the expense of engineering the UI well; or pay the expense of not engineering it well.

Javascript object not initialized on slow connections

Here's the odd situation:
we have a piece of javascript library that is being called on our onload of aspx page.
It works everytime for us, but the clients that have low speed modems get an error, because the object is not getting initialized and the aspx page is already loaded.!!
Is there any suggestions on how to call this piece of js code?
Thanks,
make sure you have your end tags.. i have seen onLoads in the not working right when your core tags are incomplete or not properly formatted
The onload even happens when everything in the page is loaded. If you have some script that is loading from a different server (ads, statistics), the onload event won't fire until those are loaded also. If their server is having problems, your onload may never fire at all, or after several minutes when the browser gives up waiting.
Instead of using onload you could put your code in a script tag as early as possible in the page, i.e. after the last element that the script needs.
If you have some external script that doesn't need a specific place in the page (statistics for example), you can move it to the bottom of the page to minimise the risk of interference with the rest of the page.
With JQuery you can call your functions with ready event :
$(document).ready(function() {
// call your functions here
});
The event will be called when the DOM is loaded.

Resources