Bind a new function to various elements on a page - javascript

This might be a dupe, but I couldn't find exactly what I was looking for. What I want is to bind a function to a named-class element on a page, then in later script, call a function against those regions. Specifically, I want to call something like (in the example below)...
$.each( $(".saveRegion"), function( idx, ele ){
var valsToSave = $(ele).getValuesToSave();
// shove the values to save into some construct
// but only for the controls inside the two divs classed as "saveRegion"
});
My page is a set of conditional includes in the Pug file. The reason that's important, is that the individual page includes might not have the same sets of controls, but that region, or parent div's children nodes, are considered a set of information. Think of a form with address information that might be a region, and gender, eye color, and height that might be a region, all included as separate Pug files if the form in question needs that information. The parent page just wants to ask all the regions "give me your information so I can compile and save it", and each of those includes should be able to respond to that.
I'm probably overdoing it, huh?
div.sampleRegions
if user.height == "tall"
include ./userTall.pug
if user.something == "another value"
include .anotherPage.pug
if user.hair == "brown"
include ./userBrownHair.pug
The html in question might look like:
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="text">
<input class="form-control" id="txtPhone" type="text">
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text">
<input class="form-control" id="txtLanguage" type="text">
</div>
</div>
So, I want to bind a function like getValuesToSave() to a div, then write the specifics for that div's getValuesToSave() function. I'm using Pug (formerly Jade) to draw forms based on certain user-specific settings, so the page includes I'm using can each know how to get and return the data for their specific page sections via some prototypical function. I hope this is making sense.
This would be a simple matter of an abstract class or a function override in any other language that supports it. I wrote C# server side stuff for systems processing for like 15 years, and this is trivial there. I am sure I'm just missing something super simple. Thanks!

You are able to use jQuery plugins for this purpose.
Example is available here.

You could go with a simple function which looks for all kind of input elements inside a specified elements and maps their values to objects of type { [nameByAttribute]: value }.
Like this:
function getValues(selector, keyAttr) {
var INPUTS = ['textarea', 'input', 'select'].join();
var BOOL_INPUTS = ['checkbox', 'radio'];
var NUMBER_INPUTS = ['range', 'number'];
var regions = [].slice.call(document.querySelectorAll(selector));
return regions.map(function(region) {
var values = [].slice.call(region.querySelectorAll(INPUTS));
return values.map(function(element, index) {
var type = element.getAttribute('type');
var key = element.getAttribute(keyAttr) || index;
var value = element.value;
if(BOOL_INPUTS.indexOf(type) > -1) value = element.checked;
if(NUMBER_INPUTS.indexOf(type) > -1) value = +element.value;
return { [key]: value };
});
});
}
document.querySelector('#save').addEventListener('click', function() {
console.log(getValues('.saveRegion', 'id'));
});
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="radio">
<input class="form-control" id="txtPhone" type="checkbox">
<input class="form-control" id="txtPhone" type="range">
<select idd="language">
<option>EN</option>
<option>DE</option>
<option>FR</option>
</select>
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text" value="textInput">
<textarea class="form-control" id="txtLanguage">textArea</textarea>
</div>
<button id="save">save</button>
</div>
This could be easily extended to take a mapping as input, which defines how certain inputs should be handled.

Ok, I think what I'm going to do is just add a function to each of the include pages that knows how to return an object containing the values for that region's salient controls, and name it based on the region name or something else to ensure uniqueness. Something like:
In Include-page-1
<div class="saveRegion" saveFunc="saveNameAddressRegion()">
<input id="val1" ..../>
<input id="val2" ..../>
<input..../>
</div>
<script>
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
</script>
Or in Jade/Pug template:
.saveRegion(saveFunc="saveNameAddressRegion()")
input#val1(type="text")
input#val2(type="text")
input#val3(type="text")
script.
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
Then in Include-page-2
<div class="saveRegion" saveFunc="doItAgain()">
<select id="selEyeColor">
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="brown">Brown</option>
</select>
</div>
<script>
function doItAgain(){
return { eyeColor : $("#selEyeColor option:selected").val() };
}
</script>
And then the enclosing/parent page can just do something like:
include ./Include-Page-1.pug
include ./Include-Page-2.pug
script.
$.each(".saveRegion", function( idx, ele ){
var vals = [];
vals.push( eval( $(ele).attr("saveFunc") ) );
// process them or whatever here.
});
This lets me get to a point where each Include file has a way of returning its values that may or may not be in some complicated on-screen form, control layout, etc. The parent page can always expect a simplified object with values, without knowing anything at all about each of the include-page layouts, design, contents, etc.
(( shrug ))

Related

Searching from DataTable

https://datatables.net/reference/api/search()#Example
Im using the link above as an example for my searching, but my setup is quite different.
<td>
<div class="plCell_desktop">
<input type="radio" class="" data-lnk_id="414107671" data-group="RUTH">
<label for="414107671">RUTH</label>
</div>
</td>
here is a extract from my table.
The only visable bit of data is "Ruth".
but when I search for say '76' it will still bring "Ruth" back as a result.
The reason is most likely the fact that I have a lot more info in the table cell than "Ruth".
Okay, so my question is. Can you force DataTables to search from the beginning of a word. e.g. ( enter "uth" will not bring back "Ruth", but "Ru" will, hope it makes sense ).
Can you do a kind of "innerHTML.val()" search with DataTables?
Okay, so my question is. Can you force DataTables to search from the
beginning of a word. e.g. ( enter "uth" will not bring back "Ruth",
but "Ru" will, hope it makes sense ).
Yes. Create a custom filter upfront that perform filtering like this. The default "smart search" will be overruled by the custom filter and any future filtering will go through that :
$.fn.dataTable.ext.search.push(function( settings, data, dataIndex ) {
var term = $('.dataTables_filter input').val().toLowerCase()
for (var i=0, l=data.length; i<l; i++) {
if (data[i].toLowerCase().indexOf(term) == 0 ) return true
}
return false
})
demo -> http://jsfiddle.net/qxcjzuxa/
As you may notice it would be very easy to make the dataTables filtering overall case sensitive (just an example). The filter array is a LIFO-structure where you can have multiple filters on top of each other. You remove a filter simply by $.fn.dataTable.ext.search.pop() if you for any reason will disable "beginning of word" filtering dynamically.
Could you just use jQuery?
$(".plCell_desktop label").each(function() {
// Using RexExp matching
RexExp regex = new RegExp(); // Your regex obj
if ($(this).val().match(regex)) {
// Do stuff if it matches
}
// Or if you just want to do something if it has a value:
if ($(this).val()) {
// ...
}
});
If you need to access the matching labels many times in your code, you may want to avoid computing the regular expression each time. One solution would be to run a one-time pre-processing that would add a custom attribute. Let's call it isOk.
You then can select the labels with a standard jQuery selector:
$('label[isOk=Y]')
Below is some demo code.
var regex = /^RU/;
$('label').each(function() {
$(this).attr('isOk', $(this).html().match(regex) !== null ? 'Y' : 'N');
});
var res = $('label[isOk=Y]');
// check whether we've selected the correct labels
res.each(function() { console.log($(this).attr('for')); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<td>
<div class="plCell_desktop">
<input type="radio" class="" data-lnk_id="414107671" data-group="RUTH">
<label for="414107671">RUTH</label>
<input type="radio" class="" data-lnk_id="414107672" data-group="RUTH">
<label for="414107672">RUTH TOO</label>
<input type="radio" class="" data-lnk_id="414107673" data-group="RUTH">
<label for="414107673">NOT REALLY RUTH</label>
</div>
</td>

Passing the value of a button into a text field

I'm having some trouble with getting Javascript to pass a value (which is stored in local storage) into a textfield. Ideally, I'd like for someone to be able to click the 'apply here' button on one page, have the job number stored in local storage and then have it auto-populate the job number field on my application page with the job number.
This is what I've got so far, I have a feeling that I haven't assigned things correctly.
html (on submit page)
<p>
<form id="applyjob1" action="enquire.html" method="get">
<input type="submit" id="job1" value="Apply for Job" />
</form>
</p>
html (field I'm trying to put data into)
Job Reference Number <input required="required" id="jobNo" name="jobno" type="text" /> </br />
Javascript
window.onload = function init() {
var jobID = document.getElementById("job"); /*button name */
jobID.onsubmit = passJob; /*executes passJob function */
}
function passJob(){
var jobSubmit = localstorage.jobID("1984"); /*assigns localstorage*/
if (jobSubmit != undefined){
document.getElementById("jobNo").value = localstorage.jobID;
}
I think this code would work for your fuction.
function passJob(){
localStorage.setItem("jobID", "1984");
if (localStorage.jobID != undefined) {
document.getElementById("jobNo").value = localStorage.jobID;
}
}
You are assigning the jobSubmit wrongly. To set item, use localStorage.setItem('key', value). Note the casing as it matters.
So basically you should do
var jobSubmit = localStorage.setItem(,"jobID", "1984"); // assigns jobSubmit
And I don't see any element with id="job"

How can I store obj properties in there current form onload?

I want to remove an obj from my form and be able to add it back as it was onload. But when I try to add it back using the var I tried to save onload I get an error (it queries the current form which has its child removed).
How can I store all the children obj properties in their current form onload, immutably, so that I can be able to add them back after they have been removed from the document?
This would give me an error ("Argument 1 of Node.appendChild is not an object"):
var fullList2 = null;
window.onload = function () {
fullList2 = document.getElementsByName('person')[0].children;
document.getElementsByName('person')[0].removeChild(document.getElementsByName('person')[0].children[1]);
document.getElementsByName('person')[0].appendChild(fullList2[1]);
}
the form:
<form name="trans" id="trans" method=POST action=entertransaction.jsp>
<input type="text" onkeyup="update();" id="input1" value="" name="filterTxt" size="21" tabindex="1" style="width: 195px" />
<br />
<select id="person" name="person" size=10 tabindex="2" style="width: 200px">
<option value="0">Scott Ambler</option>
<option value="1">Andrew Binstock</option>
</select>
<br />
<input type=submit name=action value="Next ->" tabindex="3" />
</form>
Your fullList2 is just a reference to the child array of the person select element, when you remove childs from the person select you also remove them from the fullList2 array, that is why your approach does not work.
As for the solution: you will have to store the child elements by themselves. So your code would look something like (not tested just written from the top of my head):
var fullList2 = [];
window.onload = function () {
var person= document.getElementsByName('person')[0];
//Remove objects from the dom and store them in fullList2
fullList2.push(person.removeChild(person.childNodes[0]);
fullList2.push(person.removeChild(person.childNodes[1]);
//Add them back in
person.appendChild(fullList2[0]);
person.appendChild(fullList2[1]);
}
You are running into the problem that when you assign an object to a variable, you are not making a copy of the object. So, when you delete the object, the variable that was pointing at it no longer has something to point at. You need to make a clone of the object. So, see What is the most efficient way to deep clone an object in JavaScript?

Knockout .js and changing values on click

Simplified code below. I have another build w/ajax calls that I know are working b/c I can see them hit the server. But I cannot get the stuff to render the changes back to the UI.
.html:
<div id="LoginPage" data-title="LoginPage" data-role="page" data-theme="a">
<div data-role="content" class="minimalPaddingContent">
<div class="divDivider"></div>
<h3>REACH Energy Audit Login</h3>
<input type="text" placeholder="User Name" id="userName" data-bind="value: successOrFailureSw"/>
<input type="password" placeholder="Password" id="password"/>
Sign In
<span data-bind="text: successOrFailureSw"></span>
</div>
.js:
var LoginViewModel = function (commonName, successOrFailureSw) {
var self = this;
self.commonName = ko.observable(commonName);
self.successOrFailureSw = ko.observable(successOrFailureSw);
self.changeValue = function(){
console.log("changed!");
self.successOrFailureSw = "new value!";
}
};
ko.applyBindings(new LoginViewModel("", "super fail"));
I am pretty sure my mappings are correct on the .html b/c the original value will render as super fail, and if I change the value in the text box that maps to "successOrFailureSw", I get the updated value in the span tag, but I cannot get a change of effect at click time for the login button.
I know that I am missing something so simple, so I apologize in advance.
Thanks!
brian
You assign value to the observable in wrong way. Each obserbable is a function so you should call it using () modify your changeValue function to the following:
self.changeValue = function(){
console.log("changed!");
self.successOrFailureSw("new value!");
}
You should set the value like this:
self.successOrFailureSw('new value!');
successOrFailureSw is not a string. That's why you need to set it in the same fashion that you did earlier in your code.
Try this :
self.successOrFailureSw("new value!")

Clone form and increment ID

Consider the following form:
<form>
<input type="button" value="Input Button"/>
<input type="checkbox" />
<input type="file" id="file"/>
<input type="hidden" id="hidden"/>
<input type="image" id="image" />
<input type="password" id="password" />
<input type="radio" id="radio" />
<input type="reset" id="reset" />
</form>
Utilizing Javascript (and jQuery), what would be the easiest way to clone the entire form and increment each individual id within, to ensure uniqueness.
Using jQuery I would assume you would clone the form initially via clone() and iterate through the cloned objects id and add the new id fieldname1, fieldname2 etc. However, my knowledge of jQuery isn't too great and this project is almost killing me.
Any help would be great!
You would clone() it, and before attaching the cloned element to the DOM, you'd run through and add the number to each id attribute.
(function() {
var count = 0;
window.duplicateForm = function()
var source = $('form:first'),
clone = source.clone();
clone.find(':input').attr('id', function(i, val) {
return val + count;
});
clone.appendTo('body');
count++;
};
})();
jsFiddle.
This one starts with 0, but you could easily start count with 1.
You could also use a closure if you wanted, i.e.
var cloneForm = function(form, start) {
start = start || 0;
return function() {
var clone = form.clone();
clone.find(':input').attr('id', function(i, val) {
return val + start;
});
start++;
return clone;
};
};
Then you would do...
var cloneContactForm = cloneForm($('#contact-form'), 5);
// Now I want to clone it and put it somewhere.
$(cloneContactForm()).appendTo('body');
jsFiddle.
Here's a solution without updating any ids:
Give all forms the same class
Give all fields a name
Refer to cloned forms relative to all the forms with the class
Refer to fields with their name
Example:
How about giving each cloned form a different id, and then using names for each input element?
<form class="theForm">
<input type="password" name="password" />
</form>
Then Clone it with
container.append($('.theForm:first').clone());
(or cache the first form in a variable).
Finally, access the input fields with:
$('form.theForm:eq(0) [name=password]') // password from first form
$('form.theForm:eq(1) [name=password]') // password from second form
...
If the selector lookup efficiency is a factor here then there are several trivial ways to speed it up, such as caching variables with the different forms, caching $('.theForm') and using the eq() method, etc.
Sample jsFiddle is here: http://jsfiddle.net/orip/dX4sY

Categories