I am trying to append some links with data and a click handler to a containing div.
jQuery 1.4.3
Fails in FireFox 5.0/Chrome 13.0.782, but works in IE9
I first create the element as a jQuery object, then add the data and click handler. Then I use .append() to append it to the container:
var $selector = $('Test');
$selector.data('testdata', "Test");
$selector.click(function(event) {
alert('Clicked: ' + $(this).data('testdata'));
return false;
});
$('#container').append($selector);
I see the link added, but when I click on it, the click handler does not fire.
I thought that maybe I needed to do the append first and then add data+click, but that doesn't work either:
var $selector = $('Test');
$('#container').append($selector);
$selector.data('testdata', "Test");
$selector.click(function(event) {
alert('Clicked: ' + $(this).data('testdata'));
return false;
});
Does append not preserve data and handlers? It seems that when I .append($selector), $selector and the newly added DOM object are not one in the same.
Which browser are you using? Also, which version of JQuery? This works for me in firefox, ie, and chrome on JQuery version 1.6. Here's the test fiddle I was using.
I don't think the append is the cause of your problem. It's because the html you're passing into $() to create your elements is not a simple tag.
According to the documentation of jQuery(html):
If the HTML is more complex than a single tag without attributes, as it is in the above example, the actual creation of the elements is handled by the browser's innerHTML mechanism. In most cases, jQuery creates a new element and sets the innerHTML property of the element to the HTML snippet that was passed in. When the parameter has a single tag, such as $('<img />') or $('<a></a>'), jQuery creates the element using the native JavaScript createElement() function.
This quote is from this page: http://api.jquery.com/jQuery/#jQuery2
This means that you may get a different element than what you intend because it's dependent on the browser's innerHTML property. You may find it easier if you pass in a simple tag '<a></a>' and add other attributes to it as a second argument map to $(html, props).
To get this to work with a simple tag in the $(html, props) call, you would do something like this:
var $selector = $('<a></a>',
{
"class" : "x",
"href" : "#",
text : "Test",
click: function() {
alert('Clicked: ' + $(this).data('testdata'));
return false;
}
});
$('#container').append($selector);
$selector.data('testdata', "Test data");
For my page, the problem exists using jQuery 1.4.3 and 1.4.4. But if I upgrade to 1.6.1, the problem goes away and the code works as expected. At this particular point, I am worried about upgrading to 1.6.1.
My other option is to append the element, then requery for it using jquery before adding data and handlers. That code does work, but obviously not ideal.
I ran into the same issue, try using .appendTo() instead of .append().
It worked for me.
Related
javascript experts,
i have this script in my template to load some html divs.
$(document).ready(function() {
$('body').append('<div id="wrap"><div id="wrapp-inner"><div id="wrapleft">'
+ $("#showlink").html()
+ '</div><div id="wrapright">This is text display by append</div></div></div>');
});
Now what problem i got
Now whenever i used to remove any other ID by getelemebyID script in a template then the above html all divs like wrap,inner-wrapp etc becomes completely removed. why it happened ?
Example i used the below script to remove some other IDs. but it also reflect that append body html div ids removed too. why as you see i set only slider id to remove which is not in that append body. but it reflect that append body html divs too. why ?
<script type='text/javascript'>
//<![CDATA[
$(document).ready(function() {
document.getElementById("slider").remove();
})
//]]>
</script>
working/showing the append divs when there is no "getelementbyid removed" script used for any Id: https://jsfiddle.net/83fbnwe4/15/
not working/not show the append divs when i set "getelementbyid removed" for any other iD: https://jsfiddle.net/83fbnwe4/18/
Please could you tell me why it happened. Why it reflect also the append html divs, since i only set #slider to removed, not that append bodys htmls.
Your issue is you are attempting to call the jQuery remove method on a standard DOM element. The remove method only works on jQuery objects. You really want to do this:
$(document).ready(function() {
$("#slider").remove();
});
If you do not want to use jQuery to remove the element, you can do this:
var element = document.getElementById("slider");
element.parentNode.removeChild(element);
This is how Javascript works. You cannot remove elements directly, you need to go to its parent and then use the removeChild method to remove an element. This is why jQuery uses this approach behind the scenes and makes it look a lot easier than it actually is.
In the newer versions of the Javascript specification, remove is an added method. However, it is still best practice to use the above approach or a polyfill (which uses this same approach) because Safari mobile does not support it, and none of the IE's (only Edge) supports it as well.
I work on a site that recently changed, I track certain clicks on the site through GTM and push it into the dataLayer for Google Analytics.
With the changes to the site I can't use jQuery any more so I'm having to change the following jQuery to Javascript, but I just can't get it to work. The script used to collect the h3 text within the div class 'grid_4' when the div was clicked on. The whole structure has changed now, but the old jQuery one looked like this;
<script>
var h3Tile = $("div[class*='grid_4'] a").find('h3').text();
$("div[class*='grid_4'] a").click(function() {
dataLayer.push({
'h3Value' : h3Tile,
'event' : 'tileClick'
});
});
</script>
The js I have so far is;
<script>
var outerElement = document.getElementsByClassName('ContentTeaser');
var childElems = outerElement.getElementsByTagName('h1').innerHTML;
var myFunction = function() {
dataLayer.push({
'h1Value' : childElems,
'event' : 'tileClick'
});
};
for(var i=0;i<childElems.length;i++)
childElems[i].addEventListener('click', myFunction(), false);
</script>
The only problem is that GTM refuses to accept this, saying;
'Uncaught TypeError: outerElement.getElementsByTagName is not a function'
Which I understand is related to the fact that I am creating an array rather than selecting an individual element, but I was hoping my for loop would handle this? or am I mistaken?
Thank you for any help anyone can offer.
Matt
getElementsByTagName is a method found on HTML Elements.
It and (more to the point) getElementsByClassName return an (array-like) HTML Collection, not a single HTML element.
You need to loop over outerElement and call getElementsByTagName on each element in turn instead of trying to call it on the collection itself.
Which I understand is related to the fact that I am creating an array rather than selecting an individual element, but I was hoping my for loop would handle this?
You have two collections. You are looping over the second one, but are trying to treat the first one as a single element.
It would probably be easier to simply use query selector instead:
var childElems = document.querySelectorAll(".ContentTeaser h1");
You then have a couple more problems:
for(var i=0;i<childElems.length;i++)
Since childElems is the value of innerHTML, it is undefined (if you'd called it on an element instead of an html collection then it would be a string instead) so that will throw an error.
Don't use innerHTML (which I already fixed in the query selector example).
childElems[i].addEventListener('click', myFunction(), false);
You are calling myFunction immediately and trying to assign its return value (undefined) as an event handler. Remove the ().
I have been searching this on internet, I have found some answers which were helpful like but they were not enough to solve my problem (e.g.
Similar Problem but no solution provided for my problem)
I am using JRate plugin, I am adding a div inside a div using jQuery. The problem is that when I add it using jQuery and use the JRate Functions then they are not working. But they are working without appending a new div.
I know how to make it work. I will have to use $(document) but I dont know how to use it with this code.
Here is HTML
<div class="jRate"></div>
Here is my Jquery
$(".jRate").jRate({
onSet: function(rating) {
alert(rating);
}
});
Here is my appending code
var divjRate = "<div class='jRate'></div>";
$(divjRate).appendTo('.fb-jRate');
Can any one tell me how can I use $(document) here or any other alternative solution you have.
You need to append the html element first so that it is registered in the DOM. Then, you can call jRate on it
var divjRate = "<div><div class='jRate'></div></div>";
// Append new element to container of choice
$(divjRate).appendTo('.fb-jRate');
// Use plugin on new element
$('.jRate').jRate({
onSet: function(rating) {
alert(rating);
}
});
The solution you have linked applies to binding event listeners, which is not the case with a typical jQuery plugin that usually involves DOM replacement and other things.
You will need to apply the method to newly added DOM elements. The DOM mutation event specification is deprecated due to performance issues, and it is not realistic to expect the browser to keep track of all changes (and what kind of changes) happening in the DOM.
For example, if you're adding new content with an AJAX call, you can apply the method to newly added content within the jqXHR.done() function.
Update: OP provided with some code, so I have adding a way to initialize the plugin for newly added DOM element:
// Declare new element
var divjRate = "<div><div class='jRate'></div></div>";
// Use plugin on new element
$(divjRate).find('.jRate').jRate({
onSet: function(rating) {
alert(rating);
}
});
// Append new element to container of choice
$(divjRate).appendTo('.fb-jRate');
I am attempting to fire off an AJAX call based on the onclick event for a google map integration. The info_window_content function seen here: http://jsfiddle.net/6xw2y/ is the call to create the divs that reside within the map itself.
The "v" variable does in fact contain a store_id. So in the opening line of that function, it has the following:
var info_window_string = "<div class='maps_popup' id="+v.id+">";
Now I have an onclick event that I have duplicated and modified. The first onclick event works just fine and refreshes the panel as it should. The second onclick event doesn't work and the code for that is below:
$("#div").click(function(){
var store_id = $(this).find("div").attr("id");
var pathname = "ajax=1&store_id="+store_id+"&action=get_nearby_stores&distance="+distance+"&lat="+lat+"&lng="+lng+"&products="+$('#edit-products').val();
$("#pocp_content").load("file1.php?" + pathname);
});
That doesn't seem to work. I've also tried changing the div tag to be like this:
$("div").click(function(){
Which still doesn't work. As an added side hint. At one point I was able to get it to refresh but it was passing map-container as the store_id, instead of the id itself.
What am I missing here?
I agree with Joke_Sense10,
but I think you're probably not binding the event to the right DOM element.
Try to open up the developer console in your browser (while being on the side you develop this code for), and enter $("#div") to see if the element it returns is the one you expect. You can also use console.log($("#div")) in the code for that.
answer in comments
For a larger number of elements, always use .on() method as the latter will bind an single event listener on one of the topmost nodes in the DOM tree.
$(document).on("click","#"+v.id, function(){
I am converting old jQuery version 1.2.6 code that we use with our portal (Liferay). Previously we used the livequery plugin to add events to dynamically added DOM objects. This is now a feature built-in to jQuery (the on() function). I have that figured out.
However, there was also a feature in livequery that allowed us to modify these dynamically loaded objects on load (i.e. not tied to certain events):
$(".myTextBox").livequery(function() { $(this).val("initial value"); });
I do not control the code when the ajax portlets get loaded in our portal, so I can't modify the content when created.
I've tried a few things to no avail. Here is the one that I thought would work, but doesn't. I added jQuery to my portlet so that it loads at the bottom of the portlet HTML and I added jQuery to the file.
<footer-portlet-javascript>myscript.js</footer-portlet-javascript>
...
$(document).ready(function() {
$(".myTextBox").val("initial value");
});
This doesn't work. If I write an alert($(".myTextBox")) it shows an object, but alert($(".myTextBox").val()) is undefined.
Any ideas of how I can get working?
From what I read, you want to wire up events to items that have not been necessarily added to the DOM yet at the time you're wire-up function fires. I also read that you are upgrading to a more recent version of jquery.
If you're using jquery 1.7+, the .on() method should provide this capability for you. If you're using something between 1.2.6 and 1.7, you'll need to use the .live() method to achieve this behavior.
$(".myTextBox").live('click', function(e){
console.log(this.value);
});
Optionally, you may want to mix in some AUI to do your wiring-up on the Liferay 'allPortletsReady' published event. Here is some code we've used to wire-up items once all portlets are finished loading:
//This is the AUI version of on document ready
// and is just used to form a 'sandbox'(clojure)
// around our code so the AUI object A is not modified
AUI().ready(function(A){
//This essentially subscribes the provided function
// to the Liferay custom event 'allPortletsReady'
// so that when it's fired, the provided function
// will be called.
Liferay.on('allPortletsReady', function(){
//Do your initialization here
myCustomPortletManager.init();
//OR
A.one("#mySelector").on('click', function(e){
//do your work here
});
//Etc.
//NOTE: jQuery ($) is valid inside this sandbox for our
//instance.
});
}):
Well you can setInterval to iterate checking new element.
setInterval(function(){
var $ele = $('.myTextBox:not(.loaded)'); // find new element that not loaded
if($ele.size() > 0){
$ele.each(function(){
// do stuff with elements
$(this).val("initial value");
}).addClass('loaded'); // flag this element is loaded
}
}, 200); // set delay as you wish
by the way, I'm not recommended this.
First, you probably want to use an ID instead of a class in this case to ensure you are referring specifically to a single element.
Where your alert is will determine whether this code is executed before or after. This would explain that you return an object, but no value. Put it after the value assignment, either on the page or temporally.
The following works just fine (http://jsfiddle.net/4WHyE/1/):
<input id="myid"/>
$('#myid').val('some value')
alert($('#myid').val())
If you must use class, then it depends on whether you want to set each class element individually or all to the same value. If you wish them all to have the same value, simply replace id with class in the above example:
<input class="myclass"/>
$('.myclass').val('myvalue')
If you wish to set unique values, you can simply iterate through them (http://jsfiddle.net/4WHyE/2/):
$('.myclass').each(function(index){
$(this).val('value' + index)
});