Here is the problem:
Any two Polymer elements may need to communicate. No assumption is made as to where these elements might be in the DOM (or shadow DOM), this means one event cannot simply bubble up to another element.
The good old way to achieve this would have been to let events bubble up to the root node and then fire broadcast events on the root node for other elements to listen to.
This approach however breaks encapsulation and seems to go against Polymer's overall design. AngularJS for example provides an event broadcaster that keeps controllers from unnecessarily keeping references to the root node.
Can such approach be achieved with Polymer? Otherwise can this be solved with a different approach?
You should be able to do this using polymer-signals
http://www.polymer-project.org/articles/communication.html#using-ltpolymer-signalsgt
Quoting from the doc:
Your element fires polymer-signal and names the signal in its payload:
this.fire('polymer-signal', {name: "foo", data: "Foo!"});
This event bubbles up to document where a handler constructs and dispatches a new event, polymer-signal-foo, to all instances of . Parts of your app or other Polymer elements can declare a element to catch the named signal:
<polymer-signals on-polymer-signal-foo="{{fooSignal}}"></polymer-signals>
Related
I'm new to React and am thinking of applying it to my project for more easily enforceable structure and easy DOM rendering, but I have an issue with event delegation.
In ordinary JS, if you have a container element with an indefinite number of children (e.g. <button>s), you would just put one event listener on the parent and let the event bubble up. I haven't been able to find examples of doing this with react elements, and not sure if the only way to handle events for this system is by delegating event listeners to all children, which I don't like the sound of.
Can you do event bubbling in React (and access the React element target)?
Could it be the case that when react compiles it compiles into JS that uses event listeners and event bubbling in the 'ordinary' way?
React uses event delegation by default. When you put an onClick on a React component, it doesn't set up a handler on that component's top-level DOM element; uses one on the root component or document instead. The only place I can find this mentioned in the documentation is here where, when talking about dispatching events to your components for testing purposes, it says:
...Note that you need to pass { bubbles: true } in each event you create [for testing] for it to reach the React listener because React automatically delegates events to the document.
It's also mentioned in a comment on this issue:
React doesn't attach your click event handlers to the nodes. It uses event delegation and listens at the document level.
So go ahead and do what seems right for the code you're writing. React will use event delegation under the covers.
I'm new to web components, and I've noticed some examples set the dom in the custom-element's constructor, while others do it in the connectedCallback.
As both seem to work fine(although I tried only Chrome), I assume the main difference is the case in which a user creates the element in js and not attaching it to the page?
I guess the main question here is whether I'm missing some other reason to prefer one method over the other.
Thanks.
Best practices and rules for custom element constructors
What's safe to do in the constructor
In the constructor, it's safe to
create the shadow root;
create and append elements *;
attach event listeners to those elements (scoped to your own ShadowDOM);
create attributes * (which might still be a bad idea because in the dynamic creation case this might come unexpected).
What you cannot do in the constructor
In the constructor, you are not allowed (amongst other things)
to read any attributes you haven't created beforehand...
to access child elements...
...because those might not be present in the non-upgrade case, and definitely won't be present when you dynamically create your custom element using either document.createElement('my-custom-element') or new MyCustomElement.
What's unwise to do in the constructor
In the constructor, you probably don't want to
attach event listeners to elements outside of the component's shadow DOM (like e.g. document, window), because these are the kind of listeners you should clean up in your component's disconnectedCallback (which will be called when e.g. your component is moved in the DOM).
Attaching these listeners in the constructor and properly cleaning them up in the disconnectedCallback results in missing listeners once your component gets removed from (and later re-added) or moved in the DOM.
*Pitfalls and things to be aware of
You need to be aware of the custom element lifecycle to not fall into otherwise obvious pitfalls, which include:
If you add attributes in the constructor and have included those in your component's observedAttributes, remember this will immediately trigger the attributeChangedCallback for those attributes, even if you element is not yet connected (a.k.a. in the DOM).
If you create and append other custom elements into your component's shadow DOM, remember this will trigger those components' connectedCallback.
In part, these best practices and rules follow https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance, in other parts they deviate from recommendations done there in the spec.
Specifically I disagree on the following (given the scope for the listeners is outside the component), for the reasons I gave above.
In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.
If, for example, I have a spreadsheet and want to attach handlers to cells to handle a users interaction. Typically, you would attach the handlers to the cell component itself. But if you are creating thousands of cells, this seems rather inefficient.
If I had a component hierarchy where SheetComponent -> CellComponents how can I attach a single handler to SheetComponent to handle each cell interaction? When the user interacts, I would like to have access to CellComponents props in order to identify which was clicked.
DOM events, when using React are synthetic ... they are all handled at the document level and then synthesized across all handlers. So, for DOM events, a handler hasn't been attached to each element for each event. However, that still means there is overhead that can't be avoided as your components would need to be notified that an event has occurred.
For communicating to the SheetComponent from a CellComponent, you could simply add your own event as a property: onCellActivated for example. If you have more than a couple, you could, instead of having lots of events needing to be wired up, you could have a generic event with an argument set to the type of event:
onCellEvent({ eventType: 'activation', cell: this })
That pattern starts to look more like an Action Creator/Action following the Flux pattern (without the Dispatcher and Store of course).
Instead of relying on your own events, you could use a Flux/Action/Dispatcher pattern potentially to relay key events to a centralized location (like the SheetComponent) and from there handle the interaction. With the action dispatched, you would include the key information about the cell where the interaction took place.
But, you might want to test the performance of a more traditional event model first as this might add extra complexity with little actual benefit.
You might also want to take some inspiration from Facebook's Fixed Data Table component, written for React. It's designed to handle thousands of rows of data efficiently.
The pagination controls on a page I am working on were being bound conditionally on there being more than 1 page. I don't like to see the following code in my projects,
if (pages > 1) {
$('.some_class').bind('event', function() {});
}
because I feel it represents a disorganized coding style. I would put it on the same level as sprinkling return statements here and there rather than using control. I feel like binding events to globally available objects has no place in the local scope of a function call. So what I usually do is make two javascript files, for example: pagination.js and pagination-controls.js. In the one I have logic about building the html and displaying the the pagination controls. In the other I have statements like the following:
$(document).on('click', '.pagination .next', function() {});
Which fires regardless of whether there is a $('.pagination .next') element anywhere on the page. I like the way that feels: the website has behaviours and it only knows about ids and classes, not about instance variables in some local scope somewhere.
EDIT: this is definitely bad practice, as mentioned below. However:
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document.
and the discussion on direct and delegated events is relevant. In particular I think the following describes my usage:
By picking an element that is guaranteed to be present at the time the
delegated event handler is attached, you can use delegated events to
avoid the need to frequently attach and remove event handlers. This
element could be the container element of a view in a
Model-View-Controller design, for example, or document if the event
handler wants to monitor all bubbling events in the document.
EDIT: So I guess now I'm wondering "is it bad to prefer binding behaviours to parent elements unconditionally over binding based on logic?" That's perhaps just a question of style, and my original question has been answered so I think I will accept the answer.
Yes, this is causing significant unnecessary overhead, and it is a "bad practice".
Binding your event handling to the top-level document object means that every single click that occurs on any element anywhere in your page will bubble up to the document object, where the event's target is checked to see if it matches .pagination .next.
In fact, the documentation itself recommends against your usage:
Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.
So, you're misusing on. It's for binding directly to elements or to parent elements which may have dynamically created children, and you are meant to bind to the closest possible parent element. Binding to the document is certainly not meant to be the only way you handle events in your page.
I am in the process of creating a huge web application, with a JavaScript based UI, and many events generated continuously.
To avoid bad performance due to the huge amount of the event listeners needed, I of course opted to use a single event listener which will catch all the events generated from the children elements (event bubbling).
The problem is, this application is designed in such a way that one or more modules can be loaded into the main JavaScript library I'm coding (which is responsible for controlling the UI and every other aspect of the program). Of course every module should be completely independent from each other, so you can choose which methods to load, without affecting the general functionality of the library, only adding or removing features.
Since every module can operate in different DOM elements, I need to have at least a single event listener for each module, since two modules can listen for events generated by html elements placed in different DOM branches.
http://jsfiddle.net/YRejF/2/
In this fiddle for example, the first button will let the first paragraph trigger an event, and its parent will catch it. The second button will let the second paragraph fire the event, but the div listening for the same event won't catch it, because it's not fired from one of its sons.
So my question is: is it possible to have a single event listener, able to listen also to events triggered from elements that are not its sons (elements placed everywhere on the page)?
I was thinking about having a js object, or a dom node, which store the data of the element which triggered the event, and the event itself, then a general event will be fired on the global event listener (no matter where it's placed in the dom), and it will then read the data to discover which element generated which event, and act accordingly.
Any help or suggestion about better ways of achieving this?
jQuery has a special binder for this kind of cases: live(). It let's all events bubble to the document and then handles them accordingly. However, if you use div or other containers for different panels etc, maybe using delegate() makes more sense. Don't worry too much about the number of bound elements. Believe me, it will run as well with 50 binds or 10 delegates as it will with 1 live.