I have a multi-selectable table component which contains a table-disaply component. Inside the table-display component there is another component called table-row.
table-selectable
| (contains)
v
table-display
| (contains)
v
table-row
I am doing this because I would like to make each component genereic enough so that it can be used for other purpose, Howere, I realise it is not easy to pass the action up the the parent component. The reason is that I have to carefuly wire all the actions inside sendAction method and the action name inside the hbs file and I feel this probbaly may cause error(s) in the long wrong.
My question is that is it harmful to nest tom nay components inside a single component like the one I did?
Javascript prepares an HTML DOM Tree of Objects in a document loaded. This DOM is then available to you to work around with its elements.
Javascript access to HTML DOM is still slower than executing javascript without accessing HTML DOM.
The level you have mentioned is not too deep nested, so far you are you are easy to access the inner elements.
Events generated by elements always propagate toward Execution Context which Javascript engine has created to run the piece of your code. So, you can work with these events on Global level (which is "window" level most of the time) until you are registering the listener with proper HTML element to listen to the proper event, otherwise this will not work. For example, a button fires a "click" event, e.g.
document.getElementById('mybutton').addEventListener('click', function() {
console.log('my button clicked');
});
So far as you are successful to select this button, and bind the "click" event listener, you are OK. But if your element selector is wrong, or you are trying to listen to the wrong event, this will not work.
So far as the volume of the page is concerned, the more the content, the more page load time. Also consider it as an SEO perspective.
Related
I'm integrating an HTML/CSS/JS template on a React SPA.
JS script (functions.js) uses jQuery to add event listeners when the script loads on multiple tags dynamically ($(p).addClass('.center'))
In order for it to work, the script must be added using useEffect for it to add the listeners when the component finishes rendering (in every component). Doing that, changing the route will replicate the event listeners already there for the parent component, thus, creating lag and unexpected behaviors in the app.
Here's what I have tried so far:
Having the script in index.html doesn't solve the problem. index.html loads only once, changing the route doesn't execute the script again.
Loading the script when the route changes add more duplicated event listeners since you can't destroy the old ones when the component dismounts.
Some advice on approaches I should try can help me solve the problem.
This is not recommended, and maybe not possible.
You should not manipulate the DOM from outside of React (including adding event handlers).
Anyway, you can add an extra script to a React-App, but you can not remove the script again. If this extra script is adding event handlers (and doesn't remove them),
then you can not remove the event handlers.
(You can not remove an added script because adding an extra script means
basically to execute some extra Javascript code, which can do whatever
it wants, including adding variables and functions, and even modify existing objects. You can not simply undo that.)
solutions (or not)
To achieve what you want you probably need to modify the imported script:
You can make the imported script manage the event handlers (i.e. removing them), or
you can give React the control over these event handlers (i.e. re-implement them in React),
and e.g. only keep the callbacks inside the imported script.
(Furthermore, you should make sure the script is only loaded once.)
Probably bad ideas:
If you can not remove event handlers, you may be able to remove the DOM nodes where the event handlers are attached to.
But working with the same DOM from React and from a separate script is likely to cause problems.
One might think of extracting the components that need the event handlers, and destroy them and recreate
them everytime you need a new event handler. But that sounds quite dirty to me,
and I would expect any kind of other errors later, including possibly memory leaks.
You would still re-load the same script file over and over again, only to attach a different event handler.
Old references to the DOM nodes might still stay in memory.
The custom component
I've created a custom component for the navigation of my app. It consists of an ul element and all its li elements are dynamically created based on the items in the router's navigation list. This is happening in the attached event of the component. Nothing special is going on here.
The custom attribute
But because I want to have a good looking fancy menu I also created a custom attribute and implemented it into the root ul element of the custom component. In the attached event of the custom attribute I'd like to do some dom manipulations so that my menu looks like a menu created by those cool kids.
The problem
Although the attached event of the custom attribute is fired AFTER the attached event of the custom component, the dynamically created li items are not part of the dom in the attached event of the custom attribute yet.
The question
My assumption was that on attached event the view of the actual component is attached to dom and that all components before are also attached to the dom. And when I am done with the attached event the html that has been dynamically created here is also attached. Am I mistaken?
On a side note
I am aware of using TaskQueue could solve my problem. However, I would like to know if there's a different approach/solution first because I believe that moving things in time could cause a chain of paradoxes leaving you in a maintenance nightmare.
Allow me take away your misconception about the TaskQueue.
Your idea of moving things in time causing maintenance issues would certainly apply to using setTimeout() because that incurs an actual delay, and forces execution onto the next event loop. queueTask() is more or less the same thing (it uses setTimeout() internally).
However, the TaskQueue's queueMicroTask() method works very differently.
When you call queueMicroTask(), unlike queueTask() and setTimeout(), the task is scheduled for immediate execution on the same event loop. This is a very robust mechanism with a guaranteed execution order, and it's generally considered good practice to use it within attached() before doing any DOM manipulation.
In fact, queueMicroTask() is used internally by Aurelia in various places (mostly in binding and templating-resources). Two notable places are:
Property- and collection observers use it to "delay" notifying subscribers until all other bindings have completed their internal updating work
The repeat attribute uses it to set an ignoreMutations flag (and unset it after the queue is flushed) to prevent infinite recursion while updating its inner collection
You can generally consider there to be two "phases" to the bind() and attached() hooks: a non-queued, and a queued phase. The queued phase is when components do work that relies on the whole component graph to first be done with some other (usually recursive) process.
queueMicroTask() does not delay execution, just pushes it to the end of the call stack
It's the functional equivalent of passing the function as a callback to the end of the call stack, but saves you the trouble of writing the spaghetti code required to locate that last call and wire it all up. It's super clean.
all its li elements are dynamically created based on the items in the
router's navigation list. This is happening in the attached event of
the component
See, whenever you create anything during attached(), you can't rely on that thing being there during another component's attached() as this depends on the order of compilation/composition. That's an internal matter. This is especially true for custom attributes. Custom attributes (particularly those in style libraries) use the TaskQueue all over the place because it's the only way they can rely on the DOM being done.
Using queueMicroTask() here will guarantee two things:
It's executed when aurelia is completely done with the "first pass" of attacheds and rendering
It's executed immediately when aurelia is done with that - not even a microsecond delayed.
The best (and perhaps only correct) way to address this is indeed by using the TaskQueue - I promise :)
I have a page that uses a Kendo MVVM approach for two different elements, one providing file search results, the other a document upload facility.
The problem I am encountering is to do with the change event that both elements use - it seems that when one control fires a change event it is then picked up by the other control, which then attempts to process the event and passes it on, at which point it is picked up by the second control's change handler which processes it and passes it on to the first control's change handler. As you might expect, after around 1500 repetitions of this cycle, I see a Uncaught RangeError: Maximum call stack size exceeded message as the JavaScript engine runs out of memory.
At first I thought the problem was that the container of the second model was contained within the first, but even if they are completely separate on the page it seems as though the problem still shows up, so now I'm wondering whether the problem is related to the event being global to the page.
It seems that anything I do in my event handler in terms of trying to stopPropagation or stopImmediatePropagation - or even to set the event to null altogether - makes no difference to this behaviour. Tracing the call stack I can see it looping through Kendo's trigger call then through the event binding on my object and jQuery's dispatch loops that lead it back to Kendo, where it triggers the event handler on the other observable object.
Removing my bindings does not affect the problem, the change event is still bounced back and forth between Kendo and jQuery in the same way, it just doesn't run through my code.
The answer here was not a direct consequence of Kendo itself, so it would have been hard to answer from the question as I set it.
Inside the Observable container that was raising this error, I was using Isotope for layout. The step I had missed was that I had a relationship like this:
Parent [Observable]
-> Container
-> Child
-> Child
-> Child
One of the things that Isotope brings to the party is that for each item in the child collection, it adds a reference to its parent object.
When the child is Observable that creates a structure like this:
Parent [Observable]
-> Container <--┐
-> Child ---|
-> Child ---|
-> Child ---┘
This is an ideal situation for events to be propagated from child to parent, but because the properties in question were being automagically added by the libraries in question it was very hard to troubleshoot.
The solution was to remove the Container layer from the Observable model - it didn't need to trigger anything on change and so I wrapped it in a simple getContainer() closure and used that everywhere I was previously using it as a property. This protected it from the Observable object, breaking the circular reference without harming the functionality.
It may also be relevant that as far as I can tell the initiating event was a DOM change event rather than one of Kendo's own events. The problem may have been avoidable by using a custom Kendo namespace but that would have been a significant change in a complex application and guaranteed to cause a lot of side effects.
I have a SPA ( single page application ) programmed in HTML/JavaScript/JQuery/CSS
Every time a navigation link is clicked the main div is loaded with a chunk of HTML/JavaScript/JQuery/CSS via the ajax command and the getScript function is used to load the JavaScript/JQuery portion of that chunk.
Once a user clicks on another link, the main div is removed via the remove() function and the new chunk with its JavaScript/Jquery replaces it.
HERE'S THE PROBLEM: when I load the content of the main div for the second, third, etc. time ( if a user clicks on another link but then comes back to this one ) do I run the getScript function again to load the JavaScript/JQuery? If so - wouldn't it bind the "on" and other events over each other, or does the remove() function take care of it and it's safe to do without any memory leak?
Thank you for your responses!
From http://api.jquery.com/remove/
Similar to .empty(), the .remove() method takes elements out of the DOM. Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead.
So all events bound on the div and its descendants will be removed and can be safely rebound. However if the JavaScript you are injecting binds events to things outside the div like the document, window, body etc.. multiple events will be bound which will likely cause problems if unintended.
As of jQuery 1.4, the same event handler can be bound to an element multiple times. This is especially useful when the event.data feature is being used, or when other unique data resides in a closure around the event handler function.
http://api.jquery.com/on/
If you absolutely must bind to higher level elements from the script that is run repeatedly then you should look in to using off() before you bind the events.
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.