I currently have a validation script that has a selection of <input> elements stored in objects with properties such as "id", "type" "isRequired" and"isValid". I currently have this setup() function that does the following:
function setup(obj) {
obj.getElement().onkeyup = function() {validate(obj)}
}
In order to run this setup() function on all of my input objects I need to execute the following addEvents() function
function setEvents() {
setup(firstName)
setup(lastName)
setup(email)
setup(dateOfBirth)
}
I'm helping create a system that has multiple pages of nothing but forms so I'd prefer if I didn't have to type this for each object. Is there a way I can collect an array of all the objects that are based on a specific object template? This way I could loop through the array and apply a setup to each object in a single function. If not, are there alternatives?
(p.s. I've been asking so many object-oriented(oh, I crack myself up sometimes) questions lately because this is my first time messing with objects)
---Edit---
the object template I'm referring to looks something like this:
function input(id,isRequired,type) {
this.id = id
this.isRequired = isRequired
this.type = type
}
this is then followed by a
firstName = new input('firstName',true,'alpha')
As I said in my comment, you could add the element to an array when you create it:
var inputs = [];
var firstName = new input('firstName',true,'alpha');
inputs.push(firstName);
This is not ver convenient yet. But you could create another object which manages all this:
var InputManager = {
elements: [],
create: function(/* arguments here */) {
var n = new input(/* arguments here */);
this.elements.push(n);
return n;
},
setup: function() {
for(var i = this.elements.length; i--;) {
(function(obj) {
obj.getElement().onkeyup = function() {validate(obj)};
}(this.elements[i]));
}
}
};
with which you can do:
var firstName = InputManager.create('firstName',true,'alpha');
// etc.
InputManager.setup();
Something along these lines. I think this would be a quite object oriented way. If you have a collection of objects, you often have another object which handles the functions that should be performed on all those objects.
As with most javascript questions, the easiest way to do this is with a library such as jQuery. If you have a unique way to differentiate these objects with a css selector (e.g., they all have the class "validate" or they're the only input[type="text"] fields on the page or something), then you can do a simple selection like $('.validate') to get an array of all these objects. You can get this array using javascript of course but it's a tad more complicated. Once you have the array you can loop over the elements or you can do a simple bind like $('.validate').change(validate); which will call the validate() method whenever a dom element with the class 'validate' changes.
Edit: So obviously I don't know the entirety of what you're trying to accomplish, but if you're new to web programming, just note also that no matter what you're doing on the client side (ie in the browser), all validation should also be done on the server side. Javascript validation is generally used to just be user-friendly and not to actually validate your inputs, since I could easily just turn javascript off or redefine validate as function validate() {} and bypass javascript validation for whatever reason.
2nd Edit: So I'm not sure if this answer was 100% what you're looking for but it's good to know regardless.
Judging by your examples you are not using jQuery. And for that reason alone, I'm going to up vote you. On the same note, after you get really comfortable with JS and how you can do things, really consider using a framework or saving your scripts so you don't have to reinvent the wheel for each project.
You can actually use the DOM to your advantage!
All the forms in your page can be referenced with document.forms[index]. Alternatively you can also reference a named form with document.formName.
Look at this jsfiddle for an example using the latter.
UPDATE
Reading your update and the fact that you needed a way of creating the input objects and setup the validation. I updated my fiddle with a different approach.
Used the id to hold the validation info regarding the element then the addValidation function reverts the id to it's basic form so you can still use it normally throughout your application.
The only requirement is that you addValidation the first thing after page load. So the ID get revamped first.
The solution is also JS safe, meaning if the user doesn't have JS, apart from no validation, no other things will happen.
I think your problem is that the obj in the onkeyup scope is undefined.
function setup(obj) {
//obj is desired
obj.getElement().onkeyup = function() {validate(obj) //obj is undefined because onkeyup is the new scope of this function
}
instead you could do this:
function setup(obj) {
obj.getElement().onkeyup = function() {validate(this)
}
Related
I have a Javascript testing application that uses Selenium Web Driver. When setting up the code ready to test I am always using code similar to the following that sets up properties before testing a page. In this case it is the topic page so I have this code to set up a topic object and then add properties to it:
var topic = this;
topic.topicName = element(by.id('topicName'));
topic.topicVersion = element(by.id('topicVersion'));
.....
.....
topic.description = element(by.id('description'));
topic.topicHeader = element(by.id('topicHeader'));
topic.topicFooter = element(by.id('topicFooter'));
Is there a way that I could simplify this set up so as to avoid my having to code many times the call to the element(by.id()). Ideally I would like to just pass in an object and an array of the different fields and have some code execute the element(by.id( and add the properties to the object? Note that the property names I would like to have are those that match the element Ids.
Some hints on how I could do this would be much appreciated.
You can do this :
['topicName', 'topicVersion', ...].forEach(function(k){
topic[k] = element(by.id(k));
});
A project I am working on for works wants a pure JavaScript version of the .data() implementation of jQuery.
I wanted to replicate this.
I did a search and Where is jQuery.data() stored? shows where in jQuery it is stored.
I was hoping that i could just attach a datasegment to an HTML element for accessing later.
Since jQuery is Javascript, I can look there, but it makes use of the jQuery objects, which is what I am trying to abstract. I figured there was some sort of way to associate a hash table like dataset with JavaScript and attach it to an object.
http://jsfiddle.net/npXQx/
shows you can create a .data in the object and then it is preserved. You can access it twice, and the data is there.
This example shows a simple string assignment and a function and it being called multiple times.
var item = document.getElementById("hi");
console.log(item);
item.data = {getType: function(){return this.TYPE},TYPE:"winner"};
var out = item.data.getType();
console.log("out", out);
var two = document.getElementById("hi")
console.log("should say 'winner': ", two.data.getType());
I am authoring a simple jQuery plugin that turns an input tag into a time-formatted element (on blur it will change 245p into 2:45 pm).
Since I do not want to apply the time format events to the same element twice, I need a way to detect that the specific element in the list provided has not already had the format applied.
This is the relevant part of the code:
var methods = {
init : function(sel) {
var $this = $(sel);
return $this.each(function(){
var data = $(this).data('time_formatted');
if (data) {
return;
} else {
$(this).data('time_formatted', true);
I have heard that using $(sel).data() in a plugin is not a good idea; instead, use $.data(). I don't know why, that's just what I've heard; honestly, I don't know what the difference is.
So my question is, is this the way to accomplish checking if a specific element has had the time formatter applied to it in a plugin?
If you care to see the plugin in it's current development state, see http://jsfiddle.net/userdude/xhXCR/.
Thanks!
Jared
Where have you heard that using .data() is not good? jQuery's plugin autoring page says:
Often times in plugin development, you may need to maintain state or check if your plugin has already been initialized on a given element. Using jQuery's data method is a great way to keep track of variables on a per element basis. However, rather than keeping track of a bunch of separate data calls with different names, it's best to use a single object literal to house all of your variables, and access that object by a single data namespace.
So it should be perfectly fine.
I got a simple question about jQuery but rather javascript approaches in general...
Is it ok, to do this? :
this._checkedTexts.length = 0; // <- array belonging to "me"
var contextCheckedTexts = this._checkedTexts;
$('#valueContainer input:checked').each(function() {
contextCheckedTexts.push($(this).text());
});
Since 'this' points to the element, I am using a closure here to keep additional context for the 'each'-handler around.
Is this the way 'how it's done' or could there be any risk involved using a closure in this way (memoryleak-wise....)?
What other options are there?
I find it very practical - but also I'm always a little worried about introducing reference-count-problems when using closures - which will be hard to find later on.
I don't know what you are trying to achieve but your codes may also be done like this,
this._checkedTexts.length = 0; // <- array belonging to "me"
var arr = $('#valueContainer input:checked').map(function() {
return $(this).text();
}).get();
this._checkedTexts = this._checkedTexts.concat(arr);
It seems to be fine as jQuery methods like each, map, grep does not support context by default (there're bound to the current jQuery object). However you can create you own bind function that will apply context to functions (but in this case you dontn't have to as you use default context to get the elements text).
I have an ext combobox which uses a store to suggest values to a user as they type.
An example of which can be found here: combobox example
Is there a way of making it so the suggested text list is rendered to an element in the DOM. Please note I do not mean the "applyTo" config option, as this would render the whole control, including the textbox to the DOM element.
You can use plugin for this, since you can call or even override private methods from within the plugin:
var suggested_text_plugin = {
init: function(o) {
o.onTypeAhead = function() {
// Original code from the sources goes here:
if(this.store.getCount() > 0){
var r = this.store.getAt(0);
var newValue = r.data[this.displayField];
var len = newValue.length;
var selStart = this.getRawValue().length;
if(selStart != len){
this.setRawValue(newValue);
this.selectText(selStart, newValue.length);
}
}
// Your code to display newValue in DOM
......myDom.getEl().update(newValue);
};
}
};
// in combobox code:
var cb = new Ext.form.ComboBox({
....
plugins: suggested_text_plugin,
....
});
I think it's even possible to create a whole chain of methods, calling original method before or after yours, but I haven't tried this yet.
Also, please don't push me hard for using non-standard plugin definition and invocation methodics (undocumented). It's just my way of seeing things.
EDIT:
I think the method chain could be implemented something like that (untested):
....
o.origTypeAhead = new Function(this.onTypeAhead.toSource());
// or just
o.origTypeAhead = this.onTypeAhead;
....
o.onTypeAhead = function() {
// Call original
this.origTypeAhead();
// Display value into your DOM element
...myDom....
};
#qui
Another thing to consider is that initList is not part of the API. That method could disappear or the behavior could change significantly in future releases of Ext. If you never plan on upgrading, then you don't need to worry.
So clarify, you want the selected text to render somewhere besides directly below the text input. Correct?
ComboBox is just a composite of Ext.DataView, a text input, and an optional trigger button. There isn't an official option for what you want and hacking it to make it do what you want would be really painful. So, the easiest course of action (other than finding and using some other library with a component that does exactly what you want) is to build your own with the components above:
Create a text box. You can use an Ext.form.TextField if you want, and observe the keyup event.
Create a DataView bound to your store, rendering to whatever DOM element you want. Depending on what you want, listen to the 'selectionchange' event and take whatever action you need to in response to the selection. e.g., setValue on an Ext.form.Hidden (or plain HTML input type="hidden" element).
In your keyup event listener, call the store's filter method (see doc), passing the field name and the value from the text field. e.g., store.filter('name',new RegEx(value+'.*'))
It's a little more work, but it's a lot shorter than writing your own component from scratch or hacking the ComboBox to behave like you want.
#Thevs
I think you were on the right track.
What I did was override the initList method of Combobox.
Ext.override(Ext.form.ComboBox, {
initList : function(){
If you look at the code you can see the bit where it renders the list of suggestions to a dataview. So just set the apply to the dom element you want:
this.view = new Ext.DataView({
//applyTo: this.innerList,
applyTo: "contentbox",
#qui
Ok. I thought you want an extra DOM field (in addition to existing combo field).
But your solution would override a method in the ComboBox class, isn't it? That would lead to all your combo-boxes would render to the same DOM. Using a plugin would override only one particular instance.