I currently have a page in which users can drag content around, resize it, etc.
The basic structure looks like the following:
$('.element').on().resizable().draggable();
When called at the beginning of the document load it adds bind events, resizable and draggable. Now the problem I have is that a user can add in new elements dynamically. These new elements are no longer covered by the above code. What I ended up doing is the following:
function bindEvents(){
$('.element').on().resizable().draggable();
}
bindEvents();
And then when elements are added I turn off the current implementation and then turn it on again so that it will include the new elements:
$('.element').off().resizable("destroy").draggable("destroy");
bindEvents();
No I know this is incredibly inefficient and most likely uses way more memory than needed. My question is, is there a better way to do this? Is there a way to add the one particular element to the already established group of bound elements?
Thanks!
If you don't want to waste work on the old items then brand them with something and avoid them later, or vice versa.
function bindEvents(){
$('.element').not('.initialized').addClass('initialized')
.on().resizable().draggable();
}
Why not use the delegation functionality of on() and bind to a container of the elements?
$('#container').on('mouseover', '.element', function(e) {
$(this).resizable().draggable();
});
You'd want to add the styling for the draggable classes to be there by default, but this is easily accomplished by adding the ui-resizable class to the elements:
<div class="element ui-resizable">Foo</div>
Any elements that match the filter ('.element') will automatically get the functionality when they are added to #container
Related
My code is this:
document.addEventListener('click', function(ev){
if(ev.path[0].className == 'linkTogether'){//do something}
if(ev.path[0].id == "createNewPage"){//do something}
});
Which has actually been working well for dynamically created buttons and nodes, but something just feels off about it. So I'm wondering if this is best practice or if there is a better way to add event listeners to dynamically created elements.
Thanks,
Jack
In specific cases where you have huge amount of objects that behave in the same way you can use this technique (adding event listener to their parent) to improve the performance of your script.
In a general page however you just have to many different objects and iterating trough all of them to check which one you've clicked is not faster.
Here is an article for the technique you are referring to - event delegation.
This is the proper pattern for creating event listeners that will work for dynamically-added elements. It's essentially the same approach as used by jQuery's event delegation methods (e.g. .on).
However, it does have performance implications. Every time you click anywhere in the document, the code will run, and have to go through the entire list of event bindings that you need to check. You can improve this by adding your event listener to a more specific element. If the dynamic elements are always added inside a specific DIV, add your listener to that DIV rather than document.
This also avoids another pitfall of event delegation. Event delegation depends on the event bubbling up from an inner element to all its containers. But if there are any handlers along the way that call event.stopPropagation, the event won't make it out to document. If you add the listener to a lower element, you're less likely to have a conflict like that.
And I would like to add, if you create elements dynamically, better you include the listeners inline and filter in the function what you want to do.
<div class="form_ID1" onclick="myfunction(this, event);">
... children bubbling
... Use if statements in caught js myFunction function.
In myfunction function you will capture the child element by event.target and the element that contains the listener by this.className.
There are scenarios however that you need a universal ( document ) click event. e.g You need to close a pop up when you click outside of a pop up !! It is like: e.g.
if (clicked.className != popup.className) popup.remove()
Even in this case there is a workaround by inserting an onblur="myfunction(this); in the parent DIV of the popup.
I'm trying to think of the most elegant solution to handle multiple Fx.Slides within mootools. I'm developing a dictionary page with a very long list of words where there's a pair word -- translation and all the translations must be hidden by default showing just a word list. I'm looking for a solution that won't require creating a separate slide for each word on the page, so that they're created on-the-fly when a visitor clicks on a word because the size of the script and performance hit concern me. There's another problem in that their initial states must be set to 'hidden' beforehand and I don't want to do it in CSS (that would hide everything from people whose browsers don't support javascript).
Is anything of this sort possible or will I have to rely on creating slides in a loop (my element ids go like w01, w02, ...)? If so, how would I put that block inside a loop?
Check out this question regarding if the user does not have Javascript Embedding extra styles with noscript.
After that is taken care of we can concentrate in mootools. You want the elements to have visability: hidden when you load the page with Javascript. Give your elements a class so we can select them all at once. Example to initialize the elements.
$$('.sliders').each(function(el) {
el.slide('hide').setStyle('visibility', 'visible');
});
Now we need to handle the click event. The same goes here.
Example html:
<h3 class="slideIn" >Some title</h3>
<div class="sliders>Some lengthy text<div>
Example html:
$$('.slideIn').addEvent('click', function() {
this.getNext().getChildren('.sliders').slide();
});
Example fiddle: http://jsfiddle.net/b4Zjs/
Edit: If there are a lot of elements that should have click events it's better to use event delegation. Then you are only adding one event listener to the page, and it can make a huge difference some times.
$('parent').addEvent('click:relay(h3.slideIn)', function(event, target) {
target.getNext().getChildren('.sliders').slide();
});
jsFiddle example: http://jsfiddle.net/b4Zjs/2/
Is it better to attach the on() event to the document or a closer parent?
Note: Initially this question had another aspect and a different topic. It became obsolete really quickly (typo in the source code)
The best key for performance using jQuery is to use an id as the initial identifier. For example:
$('#my_id').on('click', 'tag.my_class', function () {
...
});
This allows jQuery to go straight to the container, and then begin trawling from there.
if you bind the "on" event to the closest parent will produce exactly what are you looking for, click function will works fine even if it is appended to document, but in future if you append any elements with class "clickable" will also get binded. so its always good practice to append the "on" event to closest parent rather than whole document.
if you want more specific you can use
$("ul.media-grid").on('click', 'li.clickable', function () {
alert("works")
});
as it will get the ul with the class "media-grid" and appends the event to the li's with class "clickable"
I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.
I write a lot of dynamically generated content (developing under PHP) and I use jQuery to add extra flexibility and functionality to my projects.
Thing is that it's rather hard to add JavaScript in an unobtrusive manner. Here's an example:
You have to generate a random number of div elements each with different functionality triggered onClick. I can use the onclick attribute on my div elements to call a JS function with a parameter but that is just a bad solution. Also I could generate some jQuery code along with each div in my PHP for loop, but then again this won't be entirely unobtrusive.
So what's the solution in situations like this?
You need to add something to the divs that defines what type of behaviour they have, then use jQuery to select those divs and add the behaviour. One option is to use the class attribute, although arguably this should be used for presentation rather than behaviour. An alternative would be the rel attribute, but I usually find that you also want to specify different CSS for each behaviour, so class is probably ok in this instance.
So for instance, lets assume you want odd and even behaviour:
<div class="odd">...</div>
<div class="even">...</div>
<div class="odd">...</div>
<div class="even">...</div>
Then in jQuery:
$(document).load(function() {
$('.odd').click(function(el) {
// do stuff
});
$('.even').click(function(el) {
// dostuff
});
});
jQuery has a very powerful selector engine that can find based on any CSS based selector, and also support some XPath and its own selectors. Get to know them! http://docs.jquery.com/Selectors
I would recommend that you use this thing called "Event delegation". This is how it works.
So, if you want to update an area, say a div, and you want to handle events unobtrusively, you attach an event handler to the div itself. Use any framework you prefer to do this. The event attachment can happen at any time, regardless of if you've updated the div or not.
The event handler attached to this div will receive the event object as one of it's arguments. Using this event object, you can then figure which element triggered the event. You could update the div any number of times: events generated by the children of the div will bubble up to the div where you can catch and handle them.
This also turns out to be a huge performance optimization if you are thinking about attaching multiple handlers to many elements inside the div.
I would recommend disregarding the W3C standards and writing out HTML-properties on the elements that need handlers attached to them:
Note: this will not break the rendering of the page!
<ul>
<li handler="doAlertOne"></li>
<li handler="doAlertTwo"></li>
<li handler="doAlertThree"></li>
</ul>
Declare a few functions:
function doAlertOne() { }
function doAlertTwo() { }
function doAlertThree() { }
And then using jQuery like so:
$("ul li").each(function ()
{
switch($(this).attr("handler"))
{
case "doAlertOne":
doAlertOne();
break;
case ... etc.
}
});
Be pragmatic.
It's a bit hard to tell from your question, but perhaps you can use different jQuery selectors to set up different click behaviours? For example, say you have the following:
<div class="section-1">
<div></div>
</div>
<div class="section-2">
<div></div>
</div>
Perhaps you could do the following in jQuery:
$('.section-1 div').onclick(...one set of functionality...);
$('.section-2 div').onclick(...another set of functionality...);
Basically, decide based on context what needs to happen. You could also select all of the divs and test for some parent or child element to determine what functionality they get.
I'd have to know more about the specifics of your situation to give more focused advice, but maybe this will get you started.
I haven't don't really know about JQuery, but I do know that the DOJO toolkit does make highly unobtrusive Javascript possible.
Take a look at the example here: http://dojocampus.org/explorer/#Dojo_Query_Adding%20Events
The demo dynamically adds events to a purely html table based on classes.
Another example is the behaviour features, described here:http://dojocampus.org/content/2008/03/26/cleaning-your-markup-with-dojobehavior/