Modify dynamically added elements on load using jQuery - javascript

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)
});

Related

TypeError: $(...).on is not a function with prototype.js

I want to add a filtered listener to the change event of a form's children but I am getting weird results from the $ selector.
I call the selector with the id of the form $("exportForm") and try to call the .on(...) method on it, getting the error in question.
Inspecting the returned element I seem to find an array with numbers as ownProperties names. Indexing them in the console $(...)[1] returns the single children of the form. In the proto property there seem to be no trace of Prototype.js methods which should be added by the selector.
What is going wrong? What to look for to get it working?
PS: Prototype.js version is 1.6.1
You're right. on() was added in 1.7, so you can't use it here. What the on method gives you is "lazy evaluation". When you call
$(document).on('eventName', 'css selector', function(){ ... });
...you get an observer that doesn't have to be initialized after the page loads, or after parts of the page are replaced by Ajax callbacks. You can replace a part of the page, then click on it again, and the observer will just work.
The old way to set up an observer was this:
$(document).observe('dom:loaded', function(){
$('theId').observe('eventName', function(){
// do something
});
});
The outer observer waits until the page is finished loading, then the inner one observes some object for an event and does something with that. This will work, as long as the DOM doesn't change after it loads. If that happens, then you have to re-register the inner listener manually, since its targets have changed, and it no longer will work. The dom:loaded event only fires once, when the page itself is loaded the first time.
In your case, it seems that you want to duplicate the behavior of the Form.Element.Observer() class method in Prototype, which sets up a timed poll of all a form's child elements, and fires a callback when any of them change. You really ought to look at the documentation for that method, as it is a really bulletproof way to do what you're trying to do. But in case you really want to roll your own, here's how you would write an observer that could listen for events on multiple elements:
$(document).observe('dom:loaded', function(){
$$('#exportForm input').invoke('observe', 'change', function(evt, elm){
// evt is the event itself, you can cancel, log, whatever
// elm is a reference to the form element, you can do elm.getValue(), etc.
});
});
This uses the "double-dollar" method to get an array of elements, so any form input inside the form with the ID exportForm is captured.
invoke() applies the same callback and arguments to an array of elements, so this is setting up a separate observe method for each form element -- not efficient if you have lots of inputs! You can try to listen for the change event on the form itself, so you only have one observer method, but depending on the browsers you need to support, you may find that these events don't always bubble from the individual form input up to the form itself. You'll have to test carefully.
Each form element observed will be seen in isolation: you won't have access to the rest of the form inputs from within the callback, just that one.
That's why the Form.Element.Observer is so powerful -- it gives you a hash of the entire form each time it fires, so you can do things like create previews or validations on a frequent-enough basis to appear "live".

Triggering change on input

I have written some code that changes an input quantity on a magento 1.9 ecommerce website.
jQuery("input.qty").val("10");
The problem is the javascript that triggers the total to update doesn't fire. I have found the code responsible and it looks like this:
(function() {
var qtyFields = $('super-product-list').select('input.qty');
qtyFields.each(function(el) {
el.observe("change", updateGroupedPrice);
});
Event.observe(window, "load", updateGroupedPrice);
function updateGroupedPrice() {
//do stuff
}
})();
I think this is using prototype.js but I tried to isolate it in a codepen but couldn't get it working.
I have tried to trigger the change event like so:
jQuery("input.qty").trigger("change")
But it does not work. I also ran through a load of other events but in the dev tools it shows the code listening on "change".
Does anyone know why I can't trigger the change?
Since the page is using Prototype.js, you ought to keep using that to trigger your change. If you introduce jQuery into this, you're a) loading another complete duplicate of what Prototype already does, and b) asking for a lot of trouble isolating the fact that $() is a method in both libraries.
Your jQuery is a little fishy to me, too. You're setting the value of one picker (I imagine) and yet you are addressing it with a classname, so potentially there is more than one select.qty in the page, and all of them will change to value 10, firing off (potentially) multiple callback functions.
The Prototype code you see here is setting up a "listener" for changes on what you would address in jQuery as$(#super-product-list input.qty) inputs.
jQuery always treats $() as returning an array of objects, and thus all of its methods act on the array, even if it only contains one member. Prototype has two different methods for accessing elements in the DOM: $('some_id'), which always returns one element (or none, if no match), and $$('some css selector'), which always returns an array (of zero or more matching elements). You would write (or use native) callback methods differently, depending on which accessor you used to gather the element(s).
If you want to change one of these inputs, you will need to isolate it before you set its value.
Let's say there are three select pickers with the classname qty in your #super-product-list element. You want to change the third one to 10:
$('super-product-list').select('input.qty').last().setValue('10');
Or, much smarter than this, you add an ID to the third one, and then your code is much shorter:
$('quantity_3').setValue('10');
In either case, this will send the "change" event from your select, and the updateGroupedPrice method will observe that and do whatever you have coded it to do.
You won't need to (and should not ever) trigger the change event -- that's a "native" event, and the browser owns it. jQuery's trigger() (which is fire() in Prototype, is used exclusively for "synthetic events", like you see in Bootstrap: show.bs.modal, hide.bs.modal, etc. You can spot these by the punctuation in their names; usually dots or colons to namespace the events and avoid collisions with other code.
Finally, if you really, really, really wanted to change every single #super-product-list select.qty element on the whole page to '10', you would do this in Prototype.js:
$$('#super-product-list select.qty').invoke('setValue', 10);

Jquery is not working on newly added elements to the dom with Plugin like JRate Plugin

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');

jquery append() doesn't preserve data or click handlers

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.

Markup showing jQuery1281617118201 on Internet Explorer

I'm seeing some of the following in my markup on Internet Explorer:
<span jquery128161711820124="24"></span>
and
<span jQuery1281617118201="26"></span>
What is it?
It is a property added so that jQuery can track data associated with that element.
Things like event handlers you attach using jQuery:
$('someElement').click(function() {
// run code
});
or data you add to the element using .data()
$('someElement').data('myData', 'myValue');
are some of the associations.
jQuery doesn't add that property until it is necessary.
You can view the data associated with an element using the number at the end, as in:
jQuery1281617118201=“26”
console.log(​jQuery.cache[26]);​ // will show the data for element number 26 in the cache
I'm not 100% sure, but I think it's a property set by jQuery to speed up DOM element selection.
I would think the reason that it only appears in IE is that it laks support for a bunch of native getElements methods (ie. document.getElementByClassName)
EDIT:
I was partly right (I think). In the source code of (jQuery 1.4.2) at line 986 it's a generated attribute base on the now() method. The underlying method seems to have with cache of jQuery to do. The cache is used when selecting elements so you don't have to fetch the same element twice.
Was that code generated by your app or it's found in a third party code?
It looks like an internal jQuery variable used to maintain a state or to point to another jQuery (DOM) object.

Categories

Resources