I'm trying to call object properties dynamically. I have solved the problem with eval() but i know that eval is evil and i want to do this on a better and safer way. My eval code:
// onfocus
var classes = this.getAttribute('class').split(' ');
for(var i = 0; i < classes.length; ++i) {
if(classes[i].match(/val\- */) !== null) {
var rule = classes[i].substr(4);
var instruction = eval('validate.instructionTexts.'+ rule +'()');
tooltip.appendChild( document.createTextNode(instruction) );
}
}
And I also have this code:
// onblur
var classes = this.getAttribute('class').split(' ');
for( var i = 0; i < classes.length; ++i ){
if(classes[i].match(/val\- */) !== null) {
var rule = classes[ i ].substr( 4 );
var tooltip = document.getElementsByClassName( 'tooltip' );
for( i = 0; i < tooltip.length; ++i){
tooltip[ i ].style.display = 'none';
}
eval('validate.rules.'+ rule +'(' + (this.value) + ')');
}
the problem with the second code is that I want to send a string to my property. this.value = the text i type in my textbox so i get correct string from this.value but i got this error.
if i type foo.
Uncaught ReferenceError: foo is not defined. Javascript thinks I trying to send a variabel but i want it to send a string. How can i solve this problems?
An HTML element's CSS class can be accessed directly from JS thru the className property.
JS object properties can be accessed via the dot-notation object.property or via the square-bracket-notation object['property'].
The regex /val\- */ matches the characters v, a, l, a '-' hyphen, and zero or more spaces, anywhere in the string.
The spaces are completely irrelevant since you're testing the result of a string that was split on spaces (and so it won't contain any spaces anymore).
Also, you're not anchoring the regex so a class of 'eval-test' will also be matched. I doubt that's what you're looking for.
If you were just testing for the classes starting with val-, then the indexOf method is much easier to read, and probably also a lot more efficient.
I've adjusted your bits of code accordingly. I'm assuming that the class names for your validation rules all start with val-, and that the rest of the class name is the name for the rule:
// onfocus
var classes = this.className.split(' ');
for(var i = 0; i < classes.length; ++i) {
if(classes[i].indexOf('val-') === 0) { // the class name starts with 'val-'
var rule = classes[i].substr(4);
var instruction = validate.instructionTexts[rule]();
tooltip.appendChild(document.createTextNode(instruction));
}
}
// onblur
var classes = this.className.split(' ');
for (var i = 0; i < classes.length; ++i ){
if(classes[i].indexOf('val-') === 0) { // the class name starts with 'val-'
var rule = classes[i].substr(4);
var tooltip = document.getElementsByClassName('tooltip');
for (i = 0; i < tooltip.length; ++i){
tooltip[i].style.display = 'none';
}
validate.rules[rule](this.value);
}
}
You do not need to use eval, you can access it as:
validate.rules[rule](this.value);
Which will solve your other problem too, which is that you are passing in the value of this.value which when eval()'d is not quoted as a string (which 'foo' is) so is being interpreted as a variable.
to get a property foo from object obj, you could use either
obj.foo
or
obj["foo"]
The first one won't allow reserved words or if the property contains spaces.
So your first example could change to
validate.instructionTexts[rule]()
Related
I have this in my cshtml file:
#for (var i = 0; i < Model.Vehicles.Count; i++){
#Html.CheckBoxFor(m => Model.Vehicles[i].Selected)}
Basically Model.Vehicles is a List of vehicles and the Selected property is a bool...
I have a button that when clicked, calls this function:
function delSel(){
var vehicles = '#Html.Raw(Json.Encode(Model.Vehicles))';
var selIDs = "";
for(var i = 0; i < vehicles.length; i ++)
{
if (vehicles[i].Selected == "true") {
selIDs = selIDs + vehicles[i].ID + ",";
}
}
document.getElementById('CRVehicleIDs').value = selIDs;
}
My problem is, eventhough the Checkbox is checked, the value of the Selected property is always equal to false... Is there a better way to get the selected items in this case?
var vehicles = '#Html.Raw(Json.Encode(Model.Vehicles))';
This will not work as you are expecting, the razor syntax is rendered server side which means the value of this will not get updated when a user clicks on the checkboxes.
What you can do however is when you create you're checkboxes, give them an ID using i variable in your for loop.
#Html.CheckBoxFor(m => Model.Vehicles[i].Selected, new { id = "checkbx_" + i })
Then you can iterate through your checkboxes with a for loop.
function delSel(){
var vehicles = #Html.Raw(Json.Encode(Model.Vehicles));
var selIDs = "";
for(var i = 0; i < vehicles.length; i ++)
{
if (document.getElementById('checkbx_' + i).checked) {
selIDs = selIDs + vehicles[i].ID + ",";
}
}
document.getElementById('CRVehicleIDs').value = selIDs;
}
You're rendering this.Model.Vehicles to JSON which is then rendered within Javascript single-quotes - this will likely result in invalid Javascript syntax when accessed in a browser because JSON object property names are also enclosed in quotes.
Remove the quotes around #Html.Raw.
You would have spotted this if you looked at the rendered HTML of the page and saw that a large chunk of it would be covered in syntax errors.
Other tips:
JavaScript is typically styled with the opening brace on the same line as the keyword (as with Java), not the line below (as C#). It's also customary to use camelCase for JavaScript objects, not TitleCase. You should be able to customize your JSON-generator to use camelCase, refer to the documentation.
Boolean properties tend to be prefixed with Is, so it would be IsSelected (or isSelected in camelCase).
This is suboptimal:
if( vehicles[i].Selected == "true" ) {
Assuming that the Selected property is rendered as Javascript boolean value, you need only act on it directly:
if( vehicles[i].Selected ) {
In a Codecademy assignment, I would like to add a case to the parameter of my function, so whenever a person searches on the name "JoNes", the parameter gets "translated" to "Jones". (this is just to play with Javascript, not mandatory)
According to this website, there is a case called Sentence Case (he also calls it Title Case), however, whenever I use .toSentenceCase, it returns a syntax error on Codecademy. I would like to know if the following code would normally work and that it's Codecademy that doesn't support it in this assignment (in other words: it doesn't expect it, so anything different than the expected is wrong) OR if it's not possible to add a case to the parameter of a function like this.
Bonus: If it's the latter, how would you fix the input coming in through search();, so it always corresponds to the demand of the first letter being upper-case, while the rest is lower case?
The function:
var search = function(lastName.toSentenceCase) {
var contactsLength = contacts.length;
for (var i = 0; i < contactsLength; i++) {
if (lastName === contacts[i].lastName) {
printPerson(contacts[i]);
}
}
}
search("JoNes");
Here you go:
String.prototype.toSentenceCase= function() {
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase()
}
Then:
"JoNes".toSentenceCase();
becomes: Jones
I would try this:
String.prototype.toSentenceCase = function () {
var stringArray = this.split(" ");
for (var index = 0; index < stringArray.length; index++) {
stringArray[index] = stringArray[index].substr(0,1).toUpperCase() + stringArray[index].substr(1).toLowerCase();
}
return stringArray.join(" ");
}
//test:
var testString = new String("JoNe Is A NaMe");
console.log(testString.toSentenceCase());
I need to use native Javascript and for some of these I need to select more than one attribute (ex. a div with a class and id). Here is a code sample of what I've got so far. The example has all single selections.
var $ = function (selector) {
var elements = [];
var doc = document, i = doc.getElementsByTagName("div"),
iTwo = doc.getElementById("some_id"), // #some_id
iThree = doc.getElementsByTagName("input"),
// ^ Lets say I wanted to select an input with ID name as well. Should it not be doc.getElementsByTagName("input").getElementById("idname")
iFour = doc.getElementsByClassName("some_class"); // some_class
elements.push(i,iTwo,iThree,iFour);
return elements;
};
Oh yes, I forgot to mention I cannot use querySelector at all...
It depends on the properties you want to select on. For example, you might pass an object like:
{tagname: 'div', class: 'foo'};
and the function might be like:
function listToArray(x) {
for (var result=[], i=0, iLen=x.length; i<iLen; i++) {
result[i] = x[i];
}
return result;
}
function getByProperties(props) {
var el, elements;
var baseProps = {id:'id', tagName:'tagName'};
var result = [];
if ('tagName' in props) {
elements = listToArray(document.getElementsByTagName(props.tagName));
} else if ('id' in props) {
elements = [document.getElementById(props.id)];
}
for (var j=0, jLen=elements.length; j<jLen; j++) {
el = elements[j];
for (var prop in props) {
// Include all with tagName as used above. Avoids case sensitivity
if (prop == 'tagName' || (props.hasOwnProperty(prop) && props[prop] == el[prop])) {
result.push(el);
}
}
}
return result;
}
// e.g.
getByProperties({tagName:'div', className:'foo'});
However it's a simplistic approach, it won't do things like child or nth selectors.
You can perhaps look at someone else's selector function (there are a few around) and follow the fork to support non–qSA browsers. These are generally based on using a regular expression to tokenise a selector, then apply the selector manually similar to the above but more extensivly.
They also allow for case sensitivity for values (e.g. tagName value) and property names to some extent, as well as map HTML attribute names to DOM property names where required (e.g. class -> className, for -> htmlFor, etc.).
for (var i = 1; i < 81; i++){
if($(this).hasClass('member-'+i)){
('promote'+i) = true;
}
}
I have 80 droppable boxes. They each has an id called member-1, member-2, etc., when someone drags an item into the boxes, the variable will be turned to true and be passed to another function.
So far I found this is not working. I wasn't sure why. It is inside a droppable drop function.
since I have 80 boxes...I don't feel like typing them out manually.
Make promote an array, rather than 80 different variables. Then you can do:
var promote = [];
for (var i = 1; i < 81; i++){
if($(this).hasClass('member-'+i)){
promote[i] = true;
}
}
Much better would be to just see what classes do exist rather than testing for 81 different classes:
var matches, promotes = [], cls = this.className;
var regex = /member-(\d+)/g;
while (matches = regex.exec(cls)) {
// matches[1] contains the number from the member-xx class name
promotes.push(parseInt(matches[1], 10));
}
// promotes is an array that contain a list of the member-xx numbers that exist
// on this object
I have a plugin that is cloning an input that may or may not have the jQuery validation engine bound to it.
so, it's classes may contain e.g. validate[required,custom[number],min[0.00],max[99999.99]] or any combination of the jQuery validation engine validators.
The only for sure thing is that the class begins with validate[ and ends with ], but to make it more complicated as in the example above, there can be nested sets of [].
So, my question is, how can I remove these classes (without knowing the full class) using jQuery?
Here is my implementation, It's not using regex, but meh, who said it had too?
//'first validate[ required, custom[number], min[0.00], max[99999.99] ] other another';
var testString = $('#test')[0].className;
function removeValidateClasses(classNames) {
var startPosition = classNames.indexOf("validate["),
openCount = 0,
closeCount = 0,
endPosition = 0;
if (startPosition === -1) {
return;
}
var stringStart = classNames.substring(0, startPosition),
remainingString = classNames.substring(startPosition),
remainingSplit = remainingString.split('');
for (var i = 0; i < remainingString.length; i++) {
endPosition++;
if (remainingString[i] === '[') {
openCount++;
} else if (remainingString[i] === ']') {
closeCount++;
if (openCount === closeCount) {
break;
}
}
}
//concat the strings, without the validation part
//replace any multi-spaces with a single space
//trim any start and end spaces
return (stringStart + remainingString.substring(endPosition))
.replace(/\s\s+/g, ' ')
.replace(/^\s+|\s+$/g, '');
}
$('#test')[0].className = removeValidateClasses(testString);
It might actually be simpler to do that without JQuery. Using the className attribute, you can then get the list of classes using split(), and check whether the class contains "validate[".
var classes = $('#test')[0].className.split(' ');
var newClasses = "";
for(var i = 0; i < classes.length; i++){
if(classes[i].indexOf('validate[') === -1){
newClasses += classes[i];
}
}
$('#test')[0].className = newClasses
I think this solution is even more simple. You just have to replace field_id with the id of that element and if the element has classes like some_class different_class validate[...] it will only remove the class with validate, leaving the others behind.
var that ='#"+field_id+"';
var classes = $(that).attr('class').split(' ');
$.each(classes, function(index, thisClass){
if (thisClass.indexOf('validate') !== -1) {
$(that).removeClass(classes[index])
}
});