I am new to java script and having a problem.
I am creating a form dynamically from Json string what we obtain some how ( not relevant ) - Keeping all those controls information in Array (allParamArray in code) . A control can have parent , hence a parent control can have dependents ( children ) - I want to attach onchange event to each control which have dependents . The dependents are comma separated and taken care of in the refreshDependents function.
<code>
for (mp = 0; mp < allParamsArray.length; mp++) {
if (allParamsArray[mp].dependents) {
var parent = allParamsArray[mp].name;
var dependents = allParamsArray[mp].dependents;
document.getElementById(parent).onchange = function() {
refreshDependents(parent, dependents, this.selectedIndex)
};
}
}
</code>
The Problem is , when I change value on form for a control refreshDependents has the last parent information and not of the control which I change.
I was wondering is there any way we can achieve multiple control pointing to same onchange function and in onchange function we get the correct information about the control which is being changed ?
This should do it:
function makeHandler(parent, dependents, selectedIndex){
return function(){
refreshDependents(parent, dependents, selectedIndex);
};
}
for (mp = 0; mp < allParamsArray.length; mp++) {
if (allParamsArray[mp].dependents) {
var parent = allParamsArray[mp].name;
var dependents = allParamsArray[mp].dependents;
document.getElementById(parent).onchange = makeHandler(parent, dependents, this.selectedIndex);
}
}
The makeHandler function returns a new function object, which will keep the values that the parent, dependents, and selectedIndex variables had at the time it was created.
Related
To help visualize what I'm after. I have a button with an onclick() that increments the value of an input by 1
\\ HTML looks like this
<button class="clickme" onclick="pluspotato()">Potato</button>
<script>
var potatocount = 0;
function pluspotato() {
potatocount = potatocount + 1;
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
</script>
Now I want to add a button that will change the property of the pluspotato() function to multiply by 2.
Any help would be greatly appreciated.
If youd like to solve this properly (so that it scales for further enhancements / development) id suggest you read up on Observables. Im am going to write a simple implementation of one and explain it here.
Since you want to change a value at multiple points in your code and read it potentially at multiple points youd have to come up with some sort of interface that allows participants (eg gui elements) to listen to changes made to it (and uptdate the gui accordingly).
Since this is a quite often needed functionality it is best to write an generally applieable solution for this. Like this Class:
class Observable {
constructor(initalValue) {
// here come the subscriptions / perticepants that want to listen to changes
this.listeners = []
// this is the actual wrapped value. Which can basically be anything. The only
// important thing is that nothing changes this property outside of this class.
// A convention is to mark those properties / functions with an underscore.
this._value = initalValue
}
setValue(value) {
// first check if the current value is not already the same as the new one.
// if so: just do nothing
if (this._value === value) return
// then set the stored value (so that it can be getted)
this._value = value
// loop through all listeners and call them with the now value
for (let i = 0; i < this.listeners.length; i++) {
this.listeners[i](value)
}
}
getValue() {
return this._value
}
subscribe(func) {
// add new listeners to array so that it gets called on setValue
this.listeners.push(func)
// Optional:
// call added function immediately with current value
func(this._value)
}
unsubscribe(func) {
//should also exist
}
}
This class now allows you to add such behaviour.
let observableCounter = new Observable(0)
function incrementClick() {
observableCounter.setValue(observableCounter.getValue() + 1)
}
function doubleClick() {
observableCounter.setValue(observableCounter.getValue() * 2)
}
// then simply listen to changes everywhere you want the gui to update
function update1(value) {
console.log("Updateing GUI to " + value)
// document.getElementById()...
// Side note: dont document.getElementById here. If the element doesnt change,
// getElement once outside update1 and then simply take the reference here.
// This way on every change the element needs to be found from scartch.
}
observableCounter.subscribe(update1)
You can change the element's onclick function to a function that multiplies.
function multpotato() {
potatocount *= 2;
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
document.getElementById("change").addEventListener("click", function() {
document.querySelector(".clickme").onclick = multpotato;
});
You may do conditional operation in pluspotato() depending on activation of the second button:
var potatocount = 0;
var operation = 'add1';
function pluspotato() {
let potatocount;
if(operation === 'multiply2') {
potatocount = Number(document.getElementById("potatonum").value) * 2;
}
else{
potatocount = Number(document.getElementById("potatonum").value) + 1;
}
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
function changePluspotato() {
operation = 'multiply2';
}
<button class="clickme" onclick="pluspotato()">Potato</button>
<input id="potatonum"></input><br>
<button id="change" onclick="changePluspotato()">changePluspotato</button>
Once you click the second button, the potato button starts to multiply by 2
When a user clicks on an element in my HTML, I want a new tab to appear in their browser and the attributes of the element they clicked on to be printed in that window.
This is a typical element in the HTML:
<a id="d0e110" onclick="GetWordFile(this.id)" attr1="attr1_value" attr2="attr2_value" attr3="attr3_value">ElementContent</a>
There are many such elements. The UID is generated for each one during the XLST transformation ('id="{generate-id()}"').
This is my current javascript function:
<script>
function GetWordFile(id) {
var opened = window.open("");
opened.document.write();
}
</script>
If the last line of the function is "opened.document.write(id);", and an element is clicked, then the new window displays the correct UID for the clicked element. So the connection has been successfully established.
However, I cannot make any other attributes of the element appear in the new window. The following function, for example, produces a blank new window:
<script>
function GetWordFile(id) {
var opened = window.open("");
opened.document.write(attr1);
}
</script>
So does trying to get the attribute as a variable:
<script>
function GetWordFile(id) {
var opened = window.open("");
var a1 = getAttribute("attr1");
opened.document.write(a1);
}
</script>
I've also tried substituting inner HTML for document.write.
Can anyone identify what I am doing wrong and what my next steps should be?
You can pass this.attributes to the function, .filter() attributes that are not onclick, .map() the .value of the attributes to an array
<script>
function GetWordFile(attrs) {
attrs = [...attrs].filter(attr => {
return attr.name !== "onclick"
}).map(attr => attr.value);
console.log(attrs);
}
</script>
<a id="d0e110" onclick="GetWordFile(this.attributes)" attr1="attr1_value" attr2="attr2_value" attr3="attr3_value">ElementContent</a>
You need to call getAttribute() as a method of the element, which you can get using document.getElementById().
function GetWordFile(id) {
var el = document.getElementById(id);
var attr1 = el.getAttribute('attr1');
var opened = window.open("");
opened.document.write(attr1);
}
Instead of passing this.id as the function argument, you might consider just passing this. Why force the function to search for the element when you can just provide it directly? Then the function would be like:
function GetWordFile(el) {
var attr1 = el.getAttribute('attr1');
var opened = window.open("");
opened.document.write(attr1);
}
When you are loading a new window you are creating a new document that does not necessarily know that any other documents exist. In order for the document to know that any other documents exist you must pass in the desired information as a parameter so as mentioned above.
onclick="someFunction(this)" // which passes in the current scope
// you could then define someFunction like so to get the desired
// result
function someFunction ( htmlObject ) {
attributeList = htmlObject.attributes;
attributeArray = [...attributeList];//now can perform arrayfunctions
let a = "";
for (value in attributeArray) {
a += attributeArray[value].value + " "; // creates a string of
attributes
}
let s = window.open("");
s.document.write(a)}
Note you probably want to do some array filtering because it will return all attributes but the general principle works.
In trying to cut down on boilerplate code I'm trying to add handlers for common form fields to the handler object that will highlight the bad field, show some error, etc. So far I have this:
//FieldHelper.js
exports.withStandardStoreHandlers = function (storeObj, fields)
{
for(var i = 0; i < fields.length; i++)
{
var f = fields[i];
var midName = f.charAt(0).toUpperCase() + f.slice(1);
storeObj.prototype["onUpdate" + midName] = function(event) {
this[""+f] = event.target.value;
this[f+"ValidationState"] = '';
this[f+"HelpBlock"] = '';
};
}
return storeObj;
}
and then eventually I create an Alt store, that is exported thusly:
export default alt.createStore(FieldHelper.withStandardStoreHandlers(AddressStore, "firstName", "lastName", "street"));
And it actually does add the "onUpdateFirstName", "onUpdateLastName", etc. methods, but unfortunately they all use the field that was passed last. So in this example, I type some stuff into first name, but onUpdateFirstName modifies the street text field and its validation state/help block.
Clearly, the solution is to somehow make a copy of the "f" and "midName" variables so each of the dynamically created onUpdateX methods would have it's own value for them, instead of using the last available, but I just can't figure out how to do this in JavaScript. Preferably in as backwards-compatible way as possible, as this is going to be on the front end.
This is due to var in JavaScript being function scoped rather than block scoped as your code is expecting. A better explanation can be found here: https://stackoverflow.com/a/750506/368697
One solution is to make sure your f is declared inside a function scope on each iteration by using forEach:
//FieldHelper.js
exports.withStandardStoreHandlers = function (storeObj, fields)
{
fields.forEach(function (f) {
var midName = f.charAt(0).toUpperCase() + f.slice(1);
storeObj.prototype["onUpdate" + midName] = function(event) {
this[""+f] = event.target.value;
this[f+"ValidationState"] = '';
this[f+"HelpBlock"] = '';
};
});
return storeObj;
}
I am trying to call a javascript function that contains dynamic variable as a parameter. I think i am failing in the syntax to get this particular function executed. I have tried a few combinations and none of them seem to work. Please can someone advise..
for( i=0; i<succeedList.length; i++){
var file_uniq_id = succeedList[i].filename_uniq;
//Creating dynamic button
var subm_btn = document.createElement("INPUT");
subm_btn.setAttribute("onclick", "UploadMyScript.submitTitle(this.id,'+file_uniq_id+')");
p_titleBtn.appendChild(subm_btn);
}
UploadMyScript.submitTitle = function(id, uniqID){
// Does something ....
}
My problem is I cannot appear to pass on the 'file_uniq_id' value to UploadMyScript.submitTitle().
Why not use an event listener ?
subm_btn.addEventListener('click', function () {
UploadMyScript.submitTitle(subm_btn.id, file_uniq_id); },
false);
I`m attempting to bind an observable array of people two a two column responsive layout with click events using knockoutjs.
I have created a custom binding called TwoCol that loops through the array, and appends nodes to the DOM to create my suggested layout, but the click events are giving me trouble when I try to apply them in a custom binding nested in a loop.
I have played with it quite a bit, and encountered all types of results, but where I`m at now is calling my ~click~ event during binding, rather than on click.
http://jsfiddle.net/5SPVm/6/
HTML:
<div data-bind="TwoCol: Friends" id="" style="padding: 20px">
JAVASCRIPT:
function FriendsModel() {
var self = this;
this.Friends = ko.observableArray();
this.SelectedFriend = "";
this.SetSelected = function (person) {
alert(person);
self.SelectedFriend = person;
}
}
function isOdd(num) {
return num % 2;
}
ko.bindingHandlers.TwoCol = {
update: function (elem, valueAccessor) {
var i = 0;
var rowDiv;
var vFriends = ko.utils.unwrapObservable(valueAccessor());
$(elem).html('');
while (i < vFriends.length) {
//create row container every other iteration
if (!isOdd(i)) {
rowDiv = document.createElement("div");
$(rowDiv).addClass("row-fluid");
elem.appendChild(rowDiv);
}
//add column for every iteration
var colDiv = document.createElement("div");
$(colDiv).addClass("span6");
rowDiv.appendChild(colDiv);
//actual code has fairly complex button html here
var htmlDiv = document.createElement("div");
var htmlButton = vFriends[i]
htmlDiv.innerHTML = htmlButton;
colDiv.appendChild(htmlDiv);
//i think i need to add the event to the template too?
//$(htmlDiv).attr("data-bind", "click: { alert: $data }")
//it seems that the SetSelected Method is called while looping
ko.applyBindingsToDescendants(htmlDiv, { click: friends.SetSelected(vFriends[i]) });
i++;
}
return { controlsDescendantBindings: true };
}
}
var friends = new FriendsModel();
friends.Friends.push('bob');
friends.Friends.push('rob');
friends.Friends.push('mob');
friends.Friends.push('lob');
ko.applyBindings(friends);
I don't think you're using ko.applyBindingsToDescendants correctly. I admit I'm a little confused as to the meaning of some of the values in your code, so I may have interpreted something incorrectly.
Here's a fiddle where I think it's working the way you intended:
http://jsfiddle.net/5SPVm/7/
http://jsfiddle.net/5SPVm/8/
Notice if manually control descendant bindings (return { controlsDescendantBindings: true };), you need to set that up in the init callback, instead of update. The update callback is too late for that.
Quick rundown of the changes (edited):
Moved the controlsDescendantBindings into the init binding callback
Added the necessary parameter names to the binding param list to access additional values.
I re-enabled the html.attr call. Notice that now, because the binding context is set to the actual item, the SetSelected method doesn't exist at that level anymore, so it is necessary to use $parent.SetSelected.
$(htmlDiv).attr("data-bind", "click: $parent.SetSelected")
Fixed the ko.applyBindingsToDescendants call. This method takes a binding context, which is created from the current binding context, and also takes the element to apply the binding to. You don't want to reapply the binding, which is why this whole thing needs to be in the init handler.
var childBindingContext = bindingContext.createChildContext(vFriends[i]);
ko.applyBindingsToDescendants(childBindingContext, colDiv);