i want to do something with all the newly added divs (with some class) in the body, but i realize the .live() method does not support 'ready' eventType.
for example, this code works:
$('.new').live('click', function(){
$(this).css("background", "black");
}
but the user have to click on the div and i want to do the action automaticaly.
i tried this plugin: http://startbigthinksmall.wordpress.com/2011/04/20/announcing-jquery-live-ready-1-0-release/ but it didn't worked (action is done on the existing divs, but not on later-added ones)
You really only have two options:
Find an appropriate event fired on the div creation that you can hook into (sounds like you can't really find a suitable one)
Create a timer event that hunts for new divs and then runs the code on them (this can get processor intensive depending on the scope of the DOM that you're looking at).
Example for 2.:
window.setInterval(function() { $('.new').css("background", "black"); } }, 100);
Ideally you would want to use a faster/more specific selector than '.new' or execute it on a cached jQuery reference to a close ancestor if the page is complicated.
Couldn't you accomplish this by just using plain CSS?
<style type="text/css">
div.new { background: #000; }
</style>
I guess that wouldn't work if you want to do any other kinds of affects (fade the color, jiggle the box, whatever). In that case, I would ask - what event brings in new divs to your content? I might try to attach the div-changing event to that, or maybe make a custom event that you fire using .bind() and .trigger()..
Related
On my page, the user clicks on an element in order to edit it. To facilitate this, I assign the class editable to all such elements.
How should I listen for clicks on all these elements? Currently, I'm doing this:
document.body.addEventListener("click", (event) => {
if (event.target.classList.contains("editable")) {
// do stuff
}
});
The alternative would be to set a listener on every element, like this:
const editables = document.getElementsByClassName("editable");
for (const editable of editables) {
editable.addEventListener("click", editElement);
}
It seems to me that the first way must be better for performance, since it's only one element being listened on, but is it possible to degrade performance by attaching all such events to the body element? Are there any other considerations (e.g. browser implementations of event handling) that I'm neglecting which would suggest doing it the second way?
Short answer: definitely do it the first way. Event delegation is way more performant, but requires extra conditionals in your code, so it's basically a complexity versus performance tradeoff.
Longer Answer: For a small number of elements, adding individual event handlers works fine. However, as you add more and more event handlers, the browser's performance begins to degrade. The reason is that listening for events is memory intensive.
However, in the DOM, events "bubble up" from the most specific target to the most general triggering any event handlers along the way. Here's an example:
<html>
<body>
<div>
<a>
<img>
</a>
</div>
</body>
</html>
If you clicked on the <img> tag, that click event would fire any event handlers in this order:
img
a
div
body
html
document object
Event delegation is the technique of listening to a parent (say <div>) for a bunch of event handlers instead of the specific element you care about (say <img>). The event object will have a target property which points to the specific dom element from which the event originated (in this case <img>).
Your code for event delegation might look something like this:
$(document).ready(function(){
$('<div>').on('click', function(e) {
// check if e.target is an img tag
// do whatever in response to the image being clicked
});
});
For more information checkout Dave Walsh's blog post on Event Delegation or duckduckgo "event delegation".
NOTE ON CODE SAMPLE IN OP: In the first example, target.hasClass('editable') means that the specific thing clicked on must have the class editable for the if block to execute. As one of the commenters pointed out, that's probably not what you want. You might want to try something along these lines instead:
$(document).on('click', function(e) {
if ($(e.target).parents(".editable").length) {
// Do whatever
}
});
Let's break that down a bit:
$(e.target) - anything that on the page that was clicked converted to jQuery
.parents(".editable") - find all the ancestors of the element clicked, then filter to only include ones with the class "editable"
.length - this should be an integer. If 0, this means there are no parents with "editable" class
Another plus point for the first method
I was using the second (alternative) method that you have mentioned I noticed that when the ajax loaded... the newly created elements were not listening the event. I had to redo the for loop after ajax every time.
With the first method which looks like following in my code also works with ajax.
document.addEventListener('click', function (e) {
if (hasClass(e.target, 'classname')) {
// do stuff
}
}, false);
So first one is better
http://blog.jeremymartin.name/2008/02/building-your-first-jquery-plugin-that.html
Based on this tutorial, I created my first plugin and made some modifications.
But, the plugin doesn't work for dynamically generated content when I load new content on the site.
I have no idea how to go about making it live. The events are already using .on but the building of the pluggin doesn't work.
Here is the jsFiddle: http://jsfiddle.net/bT4dH/13/
I know I could call the plugin everytime I add content dynamically, but as that gets messy very quickly, I would prefer to avoid it, and integrate it directly into the plugin.
I was starting to try this:
$(document).on('DOMNodeInserted', function (e) {
if (e.target.classname === 'JS_postShrinker') {
$(e.target).shrinker(options);
}
});
But it doesn't work
UPDATE
Please ignore the DOMNodeInserted, I was just trying it. How do people in general make plugins live? I have the same problem with:
http://code.google.com/p/jquery-watermark/
http://unwrongest.com/projects/elastic/
And many more...
You can simply make it live by using .on() in a bit different way, with delegation. The documentation explains it in detail.
$(closeParent).on(event, selectorForElement, handler);
All you have to do is to choose a parent that you surely know will be part of the DOM when you attach the handler. In the worst case, you can choose body.
Then you attach the handler on this element, and specify the selector for the child elements (that can be inserted into the DOM at any time). The handler will run whenever the event is fired on any of the child elements matching the selector. This method makes use of event bubbling in a very clever way.
EDIT: Of course this will only work for event handling. If you need to initialize your elements when they are added to the DOM (resizing, decorating, etc.), you better call the plugin every time this happens. This is how most plugins work. The DOMNodeInserted event is not cross-browser and deprecated, like all Mutation events, so you should not use it.
There is an extra parameter (selector) to delegate the event on it:
$(document).on('DOMNodeInserted',"." + opts.clickZoneClass, function (e) {
if (e.target.classname === 'JS_postShrinker') {
$(e.target).shrinker(options);
}
});
I am using jQuery v.1.7.1 where the .live() method is apparently deprecated.
The problem I am having is that when dynamically loading html into an element using:
$('#parent').load("http://...");
If I try and add a click event afterwards it does not register the event using either of these methods:
$('#parent').click(function() ...);
or
// according to documentation this should be used instead of .live()
$('#child').on('click', function() ...);
What is the correct way to achieve this functionality? It only seems to work with .live() for me, but I shouldn't be using that method. Note that #child is a dynamically loaded element.
Thanks.
If you want the click handler to work for an element that gets loaded dynamically, then you set the event handler on a parent object (that does not get loaded dynamically) and give it a selector that matches your dynamic object like this:
$('#parent').on("click", "#child", function() {});
The event handler will be attached to the #parent object and anytime a click event bubbles up to it that originated on #child, it will fire your click handler. This is called delegated event handling (the event handling is delegated to a parent object).
It's done this way because you can attach the event to the #parent object even when the #child object does not exist yet, but when it later exists and gets clicked on, the click event will bubble up to the #parent object, it will see that it originated on #child and there is an event handler for a click on #child and fire your event.
Try this:
$('#parent').on('click', '#child', function() {
// Code
});
From the $.on() documentation:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
Your #child element doesn't exist when you call $.on() on it, so the event isn't bound (unlike $.live()). #parent, however, does exist, so binding the event to that is fine.
The second argument in my code above acts as a 'filter' to only trigger if the event bubbled up to #parent from #child.
$(document).on('click', '.selector', function() { /* do stuff */ });
EDIT: I'm providing a bit more information on how this works, because... words.
With this example, you are placing a listener on the entire document.
When you click on any element(s) matching .selector, the event bubbles up to the main document -- so long as there's no other listeners that call event.stopPropagation() method -- which would top the bubbling of an event to parent elements.
Instead of binding to a specific element or set of elements, you are listening for any events coming from elements that match the specified selector. This means you can create one listener, one time, that will automatically match currently existing elements as well as any dynamically added elements.
This is smart for a few reasons, including performance and memory utilization (in large scale applications)
EDIT:
Obviously, the closest parent element you can listen on is better, and you can use any element in place of document as long as the children you want to monitor events for are within that parent element... but that really does not have anything to do with the question.
The equivalent of .live() in 1.7 looks like this:
$(document).on('click', '#child', function() ...);
Basically, watch the document for click events and filter them for #child.
I know it's a little late for an answer, but I've created a polyfill for the .live() method. I've tested it in jQuery 1.11, and it seems to work pretty well. I know that we're supposed to implement the .on() method wherever possible, but in big projects, where it's not possible to convert all .live() calls to the equivalent .on() calls for whatever reason, the following might work:
if(jQuery && !jQuery.fn.live) {
jQuery.fn.live = function(evt, func) {
$('body').on(evt, this.selector, func);
}
}
Just include it after you load jQuery and before you call live().
.on() is for jQuery version 1.7 and above. If you have an older version, use this:
$("#SomeId").live("click",function(){
//do stuff;
});
I used 'live' in my project but one of my friend suggested that i should use 'on' instead of live.
And when i tried to use that i experienced a problem like you had.
On my pages i create buttons table rows and many dom stuff dynamically. but when i use on the magic disappeared.
The other solutions like use it like a child just calls your functions every time on every click.
But i find a way to make it happen again and here is the solution.
Write your code as:
function caller(){
$('.ObjectYouWntToCall').on("click", function() {...magic...});
}
Call caller(); after you create your object in the page like this.
$('<dom class="ObjectYouWntToCall">bla... bla...<dom>').appendTo("#whereeveryouwant");
caller();
By this way your function is called when it is supposed to not every click on the page.
Is there any way that I can call a JavaScript function via css?
For example, from this :hover style:
.first-nav li a:hover,
.first-nav li.hover a {
margin:-3px 0 -1px;
height:30px;
position:relative;
background:url(../images/nav-hover.jpg) no-repeat;
}
The JavaScript function I want to call is on an anchor :hover.
No, you can't trigger JavaScript from CSS directly.
What you can do is use CSS selectors to find the elements you want to watch in this way, and then watch for mouse events. The standard events are mouseover and mouseout, but they can be a bit tricky to work with because they bubble (you get mouseout, for instance, whenever the mouse leaves any descendant element). With appropriate logic, though, they're not to bad to work with, and in fact if you have lots of these, you probably want to use mouseover and mouseout rather than the alternative below because you can set them on just a parent container and then work out which descendant element is involved, which can be simpler in some cases (and more complicated in others).
IE provides mouseenter and mouseleave which are much easier to work with because they don't bubble, but (of course) IE-specific. These are so handy that frameworks are starting to support them even in browsers that don't; Prototype and jQuery provide them, for instance, and I wouldn't be too surprised if some other frameworks do as well. jQuery also provides the handy hover function, which would be very close to what you want:
// jQuery
$(".first-nav li a").hover(
function(event) {
// The mouse has entered the element, can reference the element via 'this'
},
function (event) {
// The mouse has left the element, can reference the element via 'this'
}
);
...which is really just a shortcut for setting up mouseenter and mouseleave handlers, but still, wonderfully concise.
In Prototype it's quite similar:
// Prototype
$$(".first-nav li a")
.invoke("observe", "mouseenter", function(event) {
// The mouse has entered the element, can reference the element via 'this'
})
.invoke("observe", "mouseleave", function(event) {
// The mouse has left the element, can reference the element via 'this'
});
(OT: In both cases, I've used anonymous inline function expressions just to avoid giving the impression you have to use named functions. I always recommend using named functions in production code, though.)
No.
(Well, Microsoft Expressions and Mozilla Bindings might allow it, but both are proprietary and should be avoided)
Assign your event handlers from JavaScript. Libraries such as YUI and jQuery allow you to pick elements to apply them to using CSS selectors. Event delegation allows you to handle events on elements without having to assign to each one explicitly (which is handy if you are adding them to the document after it loads).
You can call it from javascript(it does the same thing). Below is the code on how to do it on JS:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("a").hover(function(){
myFunctionInvoke();
});
});
</script>
or if you have particular anchor in mind just change the selector
Because it's more logical to do it from CSS.
If one wants to make links click on hover, it's an aesthetic quality and should be handled with CSS. For example:
#header_menu li a:hover{
color:white;
js:click();
}
It's both an aesthetic quality and, as such, it should be handled with CSS and a functional quality which should, as such, be handled with html and javascript.
And there is a very simple way to call a function on page load from within html:
<body onload="myFunction()">
Actually. Yes you can. Just use javascript: in the background-url like this:
<STYLE type="text/css">BODY{background:url("javascript:yourFunction();")}</STYLE>
Not entirely sure if this will work though.
Cheers!
I want to add mouse over event on all elements on the page except which has class "no_inspect",
i want to add this event after loading all page elements, i tried to write it like that:
$('*:not(.no_inspect)').mouseover(MouseOverEvent);
but its not working, seams something missed.
UPDATE Not working means:
The event is attaching to all elements on the page "have no_inspect class or haven't" which is not the behavior i want.
UPDATE MouseOverEvent Code:
function DIOnMouseOver(evt) {
element = evt.target;
// set the border around the element
element.style.borderWidth = '2px';
element.style.borderStyle = 'solid';
element.style.borderColor = '#f00';
}
The mouseover event bubbles. Try mouseenter instead.
Also, why are you applying the styles to evt.target? Why not 'this'?
function DIOnMouseOver(evt) {
$(this).css({
border: '2px solid #f00'
});
}
As mentioned by Matchu (in the comments), another way to avoid propagation is to call event.stopPropagation() within your event handler.
The code you posted should work, are you sure the mouseover event is not firing for an element that is wrapping the one you are mousing over?
Are you using the latest and greatest version of JQuery. I know older versions had some sort of class bug in the not selector.
You may want to try
$('*').not('.no_inspect').mouseover(MouseOverEvent);