I'm trying to target multiple items on a page that are loaded dynamically via javascript. The code I'm using is below and it works fine if the items are present in the DOM on load.
$(".target-item").each(function(i, element) {
var innerURL = $(this).html()
$(element).html("<img src='"+ innerURL + "'>");
});
Is this possible to do?
The code you posted will only ever work on the DOM elements currently on the page. That's the nature of scripting, it runs once and then it's over -- so anything you add later to the page will be unaffected.
You mentioned that something in your WordPress theme/plugin is responsible for adding those items to the DOM. The easiest way would be to look into that js and see if there's a way to integrate with it. Does it trigger an event after it does this (you could listen for the event and then do your thing)? Does it let you specify a callback function to be run after it does this (you could give it your img src logic as a function)? If there's no way to integrate with it ... well, that's the downside of using third-party code.
However, I think you should be able to call this logic when the elements have been added to the page, regardless of how it happens. Every DOM element triggers a 'load' event when it's loaded into the page, so you can listen for that. The elements don't exist yet, though, so you can't bind an event listener to them -- you have to use event delegation, and bind an event to the target element's parent. Here's how it might look:
var targetParent = jQuery('.some-div-that-contains-dynamic-elements');
targetParent.on('load', '.target-item', function() {
var $this = jQuery(this);
var innerURL = $this.html();
$this.html("<img src='"+ innerURL + "'>");
});
Here you're binding an event listener on the element that contains your target-items. When a new target-item is added to the DOM, it's load event fires, bubbles up to the parent, and triggers the event handler.
So you have a div or something and inside is your url that you replace with an img element right? And the Problem is that you don't know wich one is already replaced?
If I'm right than you can use ".target-item :not(:has(img))". With that selector you will get all elements that has the class .target-item but no img element as child.
Related
I am adding images to my page dynamically with jquery. Basically I am creating "pixel-y / lo-res" versions of the images on the page, and adding the at page load overtop of the originals. Then a typical "fade-out on hover" thing should fade them out on mouseover, showing the orignals.. as if "up - rezing" .. if that makes sense.
So I've got the images coming in.. but somehow the hover listener isn't attaching. At first I tried hover, now I'm on click just because it is easier to troubleshoot. Nothing.
The .on() function should attach itself even to dynamically added items, right? What is going wrong? I'm not getting any errors. It's just not working.
$.ajax({
type:"POST",
url:"js/pixelsandwich/pixelsandwich.php",
data:{src:src},
success:function(response){ // if there is already a "lo-rez" image, I get that URL.. if there isn't, one is created and I return that URL
newImg = $("<img class='crunched'>"); // create the new <img>
newImg.attr('src', response); // assign the source
frame = $that.parent(); // grab the containing frame
frame.append(newImg); // add the new image. css:position:absolute and z-index take care of the rest
}
});
$(".crunched").on("click", function(){ // << this just isn't attaching at all
alert("hello");
$(this).fadeTo('slow',0.5);
});
Cn anyone help?
The .on() function should attach itself even to dynamically added
items, right?
Nope, with dynamically created elements you have to bind using .on() to an element that already exists when the code is run. Worst case is usually the body element, but the closer an element you can pick in the DOM the better.
Assuming that .crunched is the class of your dynamically added elements, try:
$("body").on("click", ".crunched", function(){ // << this just isn't attaching at all
alert("hello");
$(this).fadeTo('slow',0.5);
});
Per the jQuery docs:
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().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page.
The on method should be used to delegate event handling to an extant ancestor of the dynamically added elements. For example:
$(document).on("click", ".crunched", function(){ // << this just isn't attaching at all
alert("hello");
$(this).fadeTo('slow',0.5);
});
The collection $(".crunched") is empty when you run your code, so the event handler isn't being attached to anything.
I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});
I have a target div el with some components displayed. These components have some events attached (on mouse over, on click). I don't control this code neither those events. They are just there. I'd like to render a widget inside this div. To do so I'm doing something like:
var save = el.innerHTML;
el.innerHTML = "my widget code";
When my widget finishes its process I want to get back to the original content so I do:
el.innerHTML = save;
The components previously saved are correctly replaced but the events attached to them don't work anymore.
I can't hide, show divs. I need to replace the innerHTML to have the exact style and positioning.
What would you do in my case?
I'm using jQuery if it helps.
Thanks.
When you serialize DOM elements back to HTML, all the event handlers and bound data are lost.
If you have DOM, work with it, not with HTML:
var save = $(el).children().detach();
$(el).html(widgetHTML);
// later
$(el).empty().append(save);
You might find .detach useful - http://api.jquery.com/detach/
It does the same as .remove but keeps all the associated jquery element data - which will include any event listeners added with jquery, though you will lose any 'regular dom events' attached.
you'd have to re-attach the element with jQuery too:
var stored = $(el).detach();
//… wait for something to finish, then reattach
stored.appendTo('#outer');
You can delegate the event to your el element since it doesn't seem to change - Those newly added elements did not exist at the time of binding
$(el).on('click','yourelementtoattachevent',function(){
// replace click with your event/events
// your code goes here
});
This is assuming you are using jQuery 1.7+, for others use
$(selector).live(); // jQuery 1.3+
$(document).delegate(selector, events, handler); // jQuery 1.4.3+
$(document).on(events, selector, handler); // jQuery 1.7+
There are some attributes of an html element that cannot be figured out until it is in the HTML DOM such as offsetHeight or offsetWidth. If I createElement('div') and want to use the div's offsetHeight, is there an event that fires when this element is appended to the document so that I know I can now use offsetHeight?
DOMNodeInserted might be what you are looking for.
See https://developer.mozilla.org/en/DOM/DOM_event_reference for details about DOM events
No, I don't think there is an event for that. But you can append a property to the newly created element telling your script it is appended. You could do something like:
function appendSignal(el,toElement){
to.appendChild(el);
el.isAppended = true;
}
var nwdiv = document.createElement('div');
// do stuff with nwdiv
appendSignal(nwdiv,document.body);
//doing other stuff
if (nwdiv.isAppended) {
/* nwdiv.offsetHeight should be available... */
}
Some browsers will initialise these attributes as soon as you've added it to the DOM. It needs to be in the DOM so CSS rules can be applied.
So you won't need to receive an event, just adding the element to the DOM as soon as it is created usually works.
But if you find the attributes you need are not initialised immediately, you can separate that code into another function (like you would for an event handler) and invoke it using setTimeout(otherFunction, 0) with no delay. Once your JavaScript has finished executing, the browser will respond to your DOM changes accordingly, and then immediately invoke your other function. This is kind of like having the event, but firing it yourself.
I have a div whose content may change in various ways: for instance its whole content may be reloaded via innerHTML, or nodes may be added via DOM methods. This in turn may happen via native Javascript or indirectly via calls the jQuery API or via other libraries.
I want to execute some code when the content of the div changes, but I have absolutely no control on how it will change. Indeed I am designing a widget that may be used by other people, who are free to change the content of their divs the way they prefer. When the inner content of this div changes, the shape of the widget may have to be updated as well.
I'm using jQuery. Is there a way to capture the event that the content of this div has changed, however it happened?
You can use DOMNodeInserted and DOMNodeRemoved to check if elements are added or removed. Unfortunately, IE doesn't support this.
$('#myDiv').bind('DOMNodeInserted DOMNodeRemoved', function(event) {
if (event.type == 'DOMNodeInserted') {
alert('Content added! Current content:' + '\n\n' + this.innerHTML);
} else {
alert('Content removed! Current content:' + '\n\n' + this.innerHTML);
}
});
Update
You could save the initial contents and future changes with .data(). Here's an example.
var div_eTypes = [],
div_changes = [];
$(function() {
$('#myDiv').each(function() {
this['data-initialContents'] = this.innerHTML;
}).bind('DOMNodeInserted DOMNodeRemoved', function(event) {
div_eTypes.concat(e.type.match(/insert|remove/));
div_changes.concat(this.innerHTML);
});
});
Example output:
> $('#myDiv').data('initialContents');
"<h1>Hello, world!</h1><p>This is an example.</p>"
> div_eTypes;
["insert", "insert", "remove"]
> div_changes;
["<iframe src='http://example.com'></iframe>", "<h4>IANA — Example domains</h4><iframe src='http://example.com'></iframe>", "<h4>IANA – Example domains</h4>"]
Update 2
You may want to include DOMSubtreeModified as well, because I've found out that DOMNodeInserted and DOMNodeRemoved don't trigger if an element's innerHTML is replaced directly. It still doesn't work in IE, but at least it works fine in other browsers.
try something like this...
$('#divId').bind('DOMNodeInserted', function(event) {
alert('inserted ' + event.target.nodeName + // new node
' in ' + event.relatedNode.nodeName); // parent
});
IE doesn't support this propert but i think the nearest to this is the propertychange event, which fires in response to a change in an attribute or CSS property of an element, but it doesn't fire in response to innerHTML changing, which would have been close to what you wanted.
one more feasible solution is to override manipulation function of jquery...
Have a look on this discussion..Preferred way of modifying elements that have yet to be created (besides events)
There is no such an event (onChange) in javascript nor jQuery, you would have to create a custom event.
One solution could be using lowpro to attach a behavior in this element you want to be controlled, this behavior would serialize the element and then build a poll that checks every x miliseconds to see if the element has changed, if changed then trigger your custom event on that element.
You have some examples on how to use lowpro with jQuery here:
http://www.learningjquery.com/2008/05/using-low-pro-for-jquery
Good luck!