jQuery.prop() does not modify DOM element? - javascript

I use an input field to submit 2 pieces of data to an event handler: the displayed value and a property value.
When I populate the data, I do this:
xInput.val('ARC'+opts[0].arc);
xInput.prop('data-sine',opts[0].sine);
// Now, trigger submit-handler
xInput.show();
xInput.trigger('change');
When I stop in the debugger, I can see that the property data-sine exists on the jQuery object and has the correct data.
Now, in the change event, I want to move the data from the "element" onto the event so the astrosearch API invocation can pick it up using a generic event handler, which also handles input from a <SELECT> tag in cases where there is more than one arc to choose from. So, I do this:
xInput.on('change', function (e) {
e.sine = $(this).attr('data-sine');
astrofinder.findAstro(e, true);
astrofinder.ui.countdown.start(function () { xInput.trigger('find'); });
});
When I stop at the first line here, this is NOT the xInput jQuery object, it's the raw HTML element - BUT the element DOES NOT HAVE a data-sine property! Somehow, it didn't make it out of jQuery into the DOM?
Why? Do I need to use a timer to get into the next event loop?

Related

How to get change event after js changes a value without modifing existing code

an input field changes its value via jquery (not by a human action), like this:
$("#code").val(9); .
How can I intercept the change event (or similar) without changing or add new lines to code that changes its value.
Programmatically setting value does not trigger any user events so you must trigger one yourself after the new value is set.
I used a change event but you could use others like the "input" event
$("#code").on('change', function(){
console.log('changed value =', this.value);
});
$("#code").val(9).change();
// or
// $("#code").val(9).trigger('change');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="code"/>

assigning value to hidden field from javascript

I am trying to do what seems to be simple but am unable to accomplish
I am trying to set the value of a column after a row focus change in a grid to a hidden value in java script.
My imbedded javascript code:
function OnGridFocusedRowChanged() {
grdA.GetRowValues(grdA.GetFocusedRowIndex(), 'ClientID', OnGetRowValues);
}
function OnGetRowValues(values) {
//Set hidden value
document.getElementById('<%=hdnClientID.ClientID%>').value = values[0];
//Fire button click
btnPopulateGrids_Click();
}
where hdnClientID is the name of my hidden field
In GridA I have the setting as such that OnGridFocusedRowChanged gets executed each time a row focus change takes place.
To this point, it works fine, the values[0] in OnGetRowValues() contains the correct value from the corresponding row in GridA.
But in the corresponding code behind, I cannot access the value from hidden field hdnClientID. Always comes up null when accessing
Current_Client_ID = CInt(hdnClientID.Value);
cannot access or convert any value from
hdnClientID.ClientID.
either.
I'm missing something simple.
I had to complete a similar task recently and I too was unable to access the value from codebehind. I researched and found out that it is not that simple and that you can't do it with javascript. What I would recommend is to check if you have jQuery installed and if you do, change your function to this:
function OnGetRowValues(values) {
//Set hidden value
$('#<%=hdnClientID.ClientID%>').val(values[0]);
//Fire button click
btnPopulateGrids_Click();
//If you change your HiddenField to have OnValueChange event, you can trigger it with this
//__doPostBack('<%= CompanyCode.ClientID %>', '');
}
Also, I guess you are firing some function from code behind with btnPopulateGrids_Click();.
I would recommend adding OnValueChanged="hdnClientID_OnValueChanged"/> to your <asp:HiddenField/> and using my supplied function triggering method.

Understanding jQuery .change event

I have some issue understanding the jQuery().change() behavior.
The HTML is basically a lot of input elements - checkboxes ( each with ID of id^='o99-post-visible-XXX' - and they are pure CSS images as Checkboxes, but this is irrelevant ) and I have another checkbox ("#o99-check-all") to "check all" and a text input field ('#o99-post-visible-ids') that receives the IDs of the selected boxes.
The jQuery code is as follows:
jQuery(document).ready(function() {
jQuery("#o99-check-all").change(function () {
jQuery("input:checkbox[id^='o99-post-visible-']").prop('checked', jQuery(this).prop("checked")).trigger('change');
});
var checkboxes = jQuery("input.o99-cbvu-posts-checkbox:checkbox");
checkboxes.on('change', function() {
// get IDS from all selected checkboxes and make a comma separated string
var ids = checkboxes.filter(':checked').map(function() {
return this.value;
}).get().join(',');
// put IDS inside a text input field
jQuery('#o99-post-visible-ids').val(ids);
// console.log(ids);
});
});
Now, more or less everything works now, but that is not the issue.
at first , the first chunk of code was:
jQuery("#o99-check-all").change(function () {
// without .trigger('change') chained
jQuery("input:checkbox[id^='o99-post-visible-']").prop('checked', jQuery(this).prop("checked"));
});
and it did not work ( why?? ) - meaning the boxes were selected as expected but the '#o99-post-visible-ids' input field was not receiving the IDs - until I chained a .trigger('change') event - when suddenly it works well.
my confusion is with the following ( which perhaps for my little understanding of jQuery internal works is counter-intuitive )
after chain adding .trigger('change') - isn't it somehow an endless loop where a chain() event is trigger inside a listener of change() ? and if not why?
Why is the code functioning now and did not function correctly before? because again, for my understanding, there was a change, even if not triggered by direct user click. Why would I need to trigger it manually?
I'm not sure I understand what you mean. What is happening now, is that whenever you change the all checkbox, the other checkboxes will be checked/unchecked the same as all, and then the change event is triggered.
Because you added a listener for change, that function will then fire. I.e. this function will run:
function() {
// get IDS from all selected checkboxes and make a comma separated string
var ids = checkboxes.filter(':checked').map(function() {
return this.value;
}).get().join(',');
// put IDS inside a text input field
jQuery('#o99-post-visible-ids').val(ids);
// console.log(ids);
}
Without your .trigger("change") (or .change() in short), you only change a property of the inputs. So the object changes, indeed, but that does not mean the change event is triggered. It does sound counter-intuitive, but events are only triggered by user actions or if you call the event explicitly - in no other way do events get triggered.
its because you have written jQuery('#o99-post-visible-ids').val(ids); inside a function which happens only when the change event done on the inputs, assigning prop directly through .prop does not trigger the change event and so the result handler wont run
Now if I understand you correctly...
...because you're giving every check box the same ID? If you wish to apply it to more than a single element, it is best practice to use a class selector instead.
jQuery(".o99-check-all").change(function () {
// without .trigger('change') chained
jQuery(".o99-check-all").prop('checked', jQuery(this).prop("checked"));
});
See link
https://api.jquery.com/change/

Fire change-event manually

Hey I've got a problem with fireing a change-event manually.
So I have a selectOneMenu (i'ts like a dropdown in jsf) with different values.
If I choose a value of this dropdown-list, a datatable should be updated. This works correctly, if i choose this value manually.
Now there is a case, where I need to insert a new value to the selectOneMenu. This new value gets selected automatically, but the change-event to update the datatable doesn't get fired...
So basically I have this button to save a new value to the selectOneMenu which then gets selected correctly, but the datatable doesn't get updated, which is why I tried to write the function fireChange() and gave that to the oncomplete of the button:
<p:commandButton ajax="true" id="seatingPlanSave" actionListener="#{EventAssistentController.createSeatingPlan}" value="#{msg.save}" update=":createEvent:EventSeatingPlan, :createEvent:ticketTypePrices" oncomplete="fireChange()"/>
For the fireChange()-function, i tried a few different things:
function fireChange() {
var element = document.getElementById("createEvent:EventSeatingPlan_input");
element.change();
}
function fireChange() {
var element = document.getElementById("createEvent:EventSeatingPlan_input");
$(element).trigger("change");
}
function fireChange() {
if ("fireEvent" in element)
element.fireEvent("onchange");
else {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
element.dispatchEvent(evt);
}
}
But none of these work :(
Can you please tell me how I can achieve this?
Thanks, Xera
You didn't tell anything about the HTML representation of createEvent:EventSeatingPlan_input while that's mandatory for us (and you!) in order to know how to let JS intercept on that. You didn't tell either if you were using <h:selectOneMenu> or <p:selectOneMenu>, so we can't take a look ourselves in the generated HTML representation. The former generates a <select><option> while the latter generates an <div><ul><li> which interacts with a hidden <select><option>. Both representations of dropdown menus require a different approach in JS. Also, information about how you're registering the change event handler function is mandatory. Is it by hardocing the onchange attribute, or by embedding a <f:ajax> or <p:ajax>?
In any way, based on the information provided so far, I'll guess that you've a
<h:selectOneMenu ...>
<f:ajax render="dataTableId" />
</h:selectOneMenu>
which will generate a <select onchange="..."><option>.
As per your first attempt:
function fireChange() {
var element = document.getElementById("createEvent:EventSeatingPlan_input");
element.change();
}
This will fail on <h:selectOneMenu> because HTMLSelectElement interface doesn't have a change property nor method. Instead, it is onchange property which returns a event handler which can directly be invoked by appending ().
The following will work on <h:selectOneMenu>:
function fireChange() {
var element = document.getElementById("createEvent:EventSeatingPlan_input");
element.onchange();
}
However this will in turn fail in <p:selectOneMenu>, because it returns a HTMLDivElement instead of HtmlSelectElement. The HTMLDivElement doesn't have a onchange property which returns an event handler. As said, the <p:selectOneMenu> generates a <div><ul><li> widget which interacts with a hidden <select><option>. You should be registering this widget in JS context and then use its special triggerChange() method.
So, given a
<p:selectOneMenu widgetVar="w_menu" ...>
<p:ajax update="dateTableId" />
</p:selectOneMenu>
this should do
function fireChange() {
w_menu.triggerChange();
}

Knockout JS: Working with old validation code that changes the DOM

See edit at the bottom.
My company has a huge code base and we want to start using knockout more effectively. However, we have validation code in place already that takes care of all aspects of client-side validation. It uses jQuery to show validation error messages and to sanitize user input.
For example, if I add the class "validate-range" to an input, it will use jQuery change/focusout events to track changes and then if a value is out of the range, it will replace it with the min/max value using $(input).val(). Since this validation code makes changes this way programmatically, my knockout view model won't be updated when these kind of changes are made.
This validation code is used everywhere in the system, and can't be replaced at the moment, so in order to use knockout, I have to make it work along side this code. What i've tried so far is creating a custom value binding that adds an additional change event handler which is used to update the view model whenever the validation code changes an input's value.
This works surprisingly well in all cases except inside a foreach binding (which is the same as using the template/with binding I would imagine). My change event handler isn't being fired on any inputs inside the foreach that use the custom value binding, even though the custom binding is being reapplied to all inputs inside the foreach every time the observable array changes.
I was hoping someone has dealt with this problem before, having to make knockout work with existing javascript code that changes DOM values, and thus doesn't update the view model. Any help is greatly appreciated.
Javascript code for custom binding, creating view model, and old validation code:
// custom value binding for amounts
ko.bindingHandlers.amountValue = {
init: function (element, valueAccessor) {
var underlyingObservable = valueAccessor(),
interceptor = ko.computed({
read: function () {
var value = underlyingObservable();
return formatAmount(value);
},
write: function (newValue) {
var current = underlyingObservable(),
valueToWrite = parseAmount(newValue);
if (valueToWrite !== current)
underlyingObservable(valueToWrite);
else if (newValue !== current.toString())
underlyingObservable.valueHasMutated();
}
});
// i apply a change event handler when applying the bindings which calls the write function of the interceptor.
// the intention is to have the change handler be called anytime the old validation code changes an input box's value via
// $(input).val("new value"); In the case of the foreach binding, whenever the observable array changes, and the table rows
// are re-rendered, this code does get ran when re-applying the bindings, however the change handler doesn't get called when values are changed.
ko.applyBindingsToNode(element, { value: interceptor, event: { change: function () { interceptor($(element).val()); } } });
}
};
// view model creation
// auto create ko view model from json sent from server
$(function () {
viewModel = ko.mapping.fromJS(jsonModel);
ko.applyBindings(viewModel);
});
// old validation code
$(document).on("focusout", ".validate-range", function () {
var $element = $(this),
val = $element.val(),
min = $element.attr("data-val-range-min"),
max = $element.attr("data-val-range-max");
if (val < min)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(min);
if (val > max)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(max);
// more code to show error message
});
HTML code that uses the custom binding inside of a foreach binding:
<table>
<thead>
<tr>
<td>Payment Amount</td>
</tr>
</thead>
<tbody data-bind="foreach: Payments">
<tr>
<td><input type="text" class="validate-range" data-val-range-min="0" data-val-range-max="9999999" data-bind="amountValue: Amount" /></td>
</tr>
</tbody>
</table>
So in the above example, if I enter "-155" in an amount text box, my custom binding runs and sets the view model Amount to -155. Then the old validation runs and re-sets the value of the textbox to "0" with $(input).val(0). My view model doesn't get updated at this point, and still reflects the -155 value. My change event handler from the custom binding is supposed to be ran to update the view model to 0, but it doesn't.
Edit:
As pointed out in the answer, .val() does not trigger any change events. The change event handler I added didn't do anything. The reason the view model was being updated when the validation code changed a value outside of the foreach binding was because we had logic somewhere else in our javascript code that was manually triggering the change event using the blur event, which in turn triggered my custom binding to run and update the view model. This blur event handler was directly bound to the text boxes, instead of being delegated, so it worked for text boxes that were there when the page is first rendered, but not for the ones dynamically inserted by the foreach binding.
For now, I just changed this logic to delegate the events within the document, so it would include dynamically inserted text boxes, and it seems to be working fine. I'm hoping to come up with a better solution in the future.
Calling $(element).val("some value"); does not trigger the change event.
You would need to do: $(element).val("some value").change();

Categories