I'd like to change the default behavior of the client-side validation that ASP.NET MVC 3 uses in this way:
If there is an error, I don't want any span added to the HTML to show me my mistake.
The only thing I want is to change the color of the input field which has the error.
I don't know if this is the "right" way to do it but this is what I already did:
I edited the jquery.validate.unobtrusive.js:
function onError( error, inputElement )
{ // 'this' is the form element
var container = $( this ).find( "[data-valmsg-for='" + inputElement[0].name + "']" ),
replace = $.parseJSON( container.attr( "data-valmsg-replace" ) ) !== false;
container.removeClass( "field-validation-valid" ).addClass( "field-validation-error" );
inputElement.addClass( "custom-validation-error" ); // <--------- this is my edit
error.data( "unobtrusiveContainer", container );
if( replace )
{
container.empty();
error.removeClass( "input-validation-error" ).appendTo( container );
}
else
{
error.hide();
}
}
This only adds the class named custom-validation-error which changes the color of the input field.
But, how do I remove this class, if everything is ok?
And how do I stop from adding the spans to my HTML?
Thanks.
Simply remove the Html.ValidationMessageFor field which is responsible for adding the <span> tag in which the error message will be shown. So all you need is a Html.EditorFor. Then get rid of the javascript you have written.
Ok the answer was REALLY simple..
I just added this CSS rule:
.custom-validation-error
{
border: 1px solid red !important;
}
removed my javascript code and removed the Html.ValidationMessageFor as Darin Dimitrov suggested.
Damn, damn...
Related
My first time writing my own javascript/jQuery for-loop and I'm running into trouble.
Basically, I have a series of divs which are empty, but when a button is clicked, the divs turn into input fields for the user. The input fields are there at the outset, but I'm using CSS to hide them and using JS/jQuery to evaluate the css property and make them visible/hide upon a button click.
I can do this fine by putting an id tag on each of the 7 input fields and writing out the jQuery by hand, like this:
$('#tryBTN').click(function(){
if ( $('#password').css('visibility') == 'hidden' )
$('#password').css('visibility','visible');
else
$('#password').css('visibility','hidden');
}
Copy/pasting that code 7 times and just swapping out the div IDs works great, however, being more efficient, I know there's a way to put this in a for-loop.
Writing this code as a test, it worked on the first one just fine:
$('#tryBTN').click(function() {
for(i = 1; i <= 7; i++) {
if($('#input1').css('visibility') == 'hidden')
$('#input1').css('visibility', 'visible');
}
});
But again, this only works for the one id. So I changed all the HTML id tags from unique ones to like id="intput1" - all the way out to seven so that I could iterate over the tags with an eval. I came up with this:
$('#tryBTN').click(function () {
for (i = 1; i <= 7; i++) {
if ($(eval('input' + i)).css('visibility') == 'hidden')
$('input' + i).css('visibility', 'visible');
}
});
When I put in the eval stuff - it doesn't work. Not sure what I'm doing wrong. A sample of the HTML looks like this:
<form>
<div class="form-group">
<label for="page">Description: Specifies page to return if paging is selected. Defaults to no paging.</label>
<input type="text" class="form-control" id="input7" aria-describedby="page">
</div>
</form>
You were forgetting the #:
$('#tryBTN').click(function () {
for (i = 1; i <= 7; i++) {
var el = $('#input' + i); // <-- The needed `#`
if (el.css('visibility') == 'hidden') {
el.css('visibility', 'visible');
}
}
});
#Intervalia's answer explains the simple error in your code (the missing #), and the comments explain why you should never use eval() unless you absolutely know it's the right tool for the job - which is very rare.
I would like to add a suggestion that will simplify your code and make it more reliable.
Instead of manually setting sequential IDs on each of your input elements, I suggest giving them all a common class. Then you can let jQuery loop through them and you won't have to worry about updating the 7 if you ever add or remove an item.
This class can be in addition to any other classes you already have on the elements. I'll call it showme:
<input type="text" class="form-control showme" aria-describedby="page">
Now you can use $('.showme') to get a jQuery object containing all the elments that have this class.
If you have to run some logic on each matching element, you would use .each(), like this:
$('#tryBTN').click( function() {
$('.showme').each( function( i, element ) {
if( $(element).css('visibility') == 'hidden' ) {
$(element).css( 'visibility', 'visible' );
}
});
});
But you don't need to check whether an element has visibility:hidden before changing it to visibility:visible. You can just go ahead and set the new value. So you can simplify the code to:
$('#tryBTN').click( function() {
$('.showme').each( function( i, element ) {
$(element).css( 'visibility', 'visible' );
});
});
And now that the only thing we're doing inside the loop is setting the new visibility, we don't even need .each(), since jQuery will do the loop for us when we call .css(). (Thanks #TemaniAfif for the reminder.)
So the code becomes very simple:
$('#tryBTN').click( function() {
$('.showme').css( 'visibility', 'visible' );
});
I currently have a button that makes the text in a textarea bold it uses
<a href="#" class="stylingButtons" type="button" onclick="boldTextArea()" value="Bold Text"}>
<img alt='Bold Button' src='BButton.gif' border='4' width="90px" >
</a>
for the HTML section and
function boldTextArea(){
document.getElementById("TextArea").style.fontWeight="bolder";;
}
for the javascript
Is there anyway I can edit this coding so that once the text in the text area is already bold, I could click again for it to become unbold?
I'm really not keen on the idea of using jQuery as I have never had any experience with it, but I am open to to suggestions in this format?
if you dont want to use jquery , here is a quick'n'dirty javascript
function boldText(){
var target = document.getElementById("TextArea");
if( target.style.fontWeight == "bolder" ) {
target.style.fontWeight = "normal";
} else {
target.style.fontWeight = "bolder";
}
}
should work just fine.
this is the basic way of switching from bold to normal and back i guess.
also notice the DOUBLE ";" in yout JS.
hope this helps.
g.r. Ace
UPDATE
here is the jQuery version to change ( toggle ) the text size ( or any other css attribute ) on each click..
$("#textboxid").click( function(e) {
if( $(this).css("font-size") == "12px" ) {
$(this).css("font-size", "14px")
} else {
$(this).css("font-size", "12px")
}
});
replace the css attribute to yout likes if you want to change any other attributes.
I just did this on a site of mine and found the answer here:
Press button to "Bold" & press again to "Unbold"
Hope this helps.
Putting together a very basic query builder using or, not and () operators. I have them appending to an input field though I think it would be beneficial to the user if when the brackets operator button was appended to the input field the cursor would be placed (here) ready for the users query.
So far I have:
// query builder functions
$(function() {
var queryinput = $( "#query-builder-modal .search-box input.querybuilder" );
$('#query-builder-modal ul.operators li.or').click(function(){
queryinput.val( queryinput.val() + "OR" );
queryinput.focus();
});
$('#query-builder-modal ul.operators li.not').click(function(){
queryinput.val( queryinput.val() + "-" );
queryinput.focus();
});
$('#query-builder-modal ul.operators li.brackets').click(function(){
queryinput.val( queryinput.val() + "()" );
queryinput.focus();
});
});
Can anyone help me plave the cursor between the brackets in the third click function of this sample code?
Thanks in advance
You can try something like this :
$('#query-builder-modal ul.operators li.brackets').click(function(){
queryinput.val( queryinput.val() + "()" );
var pos = queryinput.val().length - 1;
myInput.focus(); /* Seems mandatory for Firefox/Opera */
queryinput[0].selectionStart = queryinput[0].selectionEnd = pos;
});
jsfiddle example here
EDIT: It seems that for Firefox and Opera, you must first focus() your input but not on Safari and chrome. I updated the code and the jsfiddle.
(jQuery noob here)
Im trying to write a script which when I write <input type='checkbox'/> will automatically convert it to jQuery UI button and look like a checkBox.
Sample code so far ...
var newCheckboxID = 0;
$( "input:checkbox" ).attr('id', "cbx-" + nextCheckboxID++); // how to do that?
$( "input:checkbox" ).after("<label style='width:16px; height:16px; vertical-align:middle;'></label>");
$( "input:checkbox" ).next().attr("for", $(this).attr('id') ); // doesn't work for sure
$( "input:checkbox" ).button();
$( "input:checkbox" ).button( "option", "text", false );
$( "input:checkbox" ).attr("onclick", "$(this).button( 'option', 'icons', {primary:((this.checked)?'ui-icon-check':null),secondary:null} )");
Sorry, if it's too obvious but I've lost more than hour in that ...
EDIT
Finally did it with the old fashioned way (for the doesn't working parts).
Any comments for making it more compact and "more jQuery" would be appriciated ...
Code sample
// ---- set ids
var checkboxID = 0;
//$( "input:checkbox" ).attr('id', "cbx-" + nextCheckboxID++); // how to do that?
var cboxes = document.getElementsByTagName('input'); // <-- do this instead
for(var i=0; i<cboxes.length; i++){
if( cboxes[i].getAttribute('type')!='checkbox' ) continue;
cboxes[i].setAttribute('id', 'cbx-'+checkboxID++);}
// ---- add labels
$( "input:checkbox" ).after("<label style='width:16px; height:16px; vertical-align:middle;'></label>");
//$( "input:checkbox" ).next().attr("for", $(this).attr('id') ); // doesn't work this
for(var i=0; i<cboxes.length; i++){ // <-- do this instead
if( cboxes[i].getAttribute('type')!='checkbox' ) continue;
cboxes[i].nextSibling.setAttribute('for', cboxes[i].getAttribute('id') );}
// ---- create
$( "input:checkbox" ).button();
$( "input:checkbox" ).button( "option", "text", false );
$( "input:checkbox" ).attr("onclick", "$(this).button( 'option', 'icons', {primary:((this.checked)?'ui-icon-check':null),secondary:null} )");
Working examples:
jsFiddle (without comments)
jsFiddle (without comments with UI theme switcher!)
jsFiddle (with comments)
jsFiddle (Just for fun, uses timer and some other jQuery features, just for future reference)
jsFiddle (Just for fun, uses timer and changes UI theme every second!)
In the following, I should note 2 primary changes. I added CSS to do what you were trying to do to labels in "code" (where it really doesn't belong).
Also, I changed the HTML for "ease of jQuery" use. However, I still noted in the comments how you can easily change it back.
the HTML
<center>
<button>Create New CheckBox</button>
</center>
<hr />
<div id="CheckBoxes">
<input class="inp-checkbox" />
<input class="inp-checkbox" />
<input class="inp-checkbox" />
<input class="inp-checkbox" />
</div>
the CSS
.inp-checkbox+label {
width:16px;
height:16px;
vertical-align:middle;
}
the JavaScript/jQuery
// keep in mind, and i will explain, some of these "moving-parts" or not needed, but are added to show you the "ease" of jquery and help you see the solution
// This global function is designed simply to allow the creation of new checkboxes as you specified, however, if you won't be making check boxes at end user time, then i suggest simply moving it to within the .each statement found later on.
// Also, this could easily be written as a jQuery plugin so that you could make a "chainable" one-line call to change checkboxes to this but let's get to the nitty gritty first
function createCheckBox(ele, i) {
// First I simply create the new ID here, of course you can do this inline, but this gives us a bottleneck for possible errors
var newID = "cbx-"+i;
// below we use the param "ele" wich will be a jQuery Element object like $("#eleID")
// This gives us the "chainability" we want so we don't need to waste time writing more lines to recall our element
// You will also notice, the first thing i do is asign the "attribute" ID
ele.attr({ "id": newID })
// Here we see "chainability at work, by not closing the last line, we can move right on to the next bit of code to apply to our element
// In this case, I'm changing a "property", keep in mind this is kinda new to jQuery,
// In older versions, you would have used .attr but now jQuery distinguishes between "attributes" and "properties" on elements (note we are using "edge", aka. the latest jQuery version
.prop({ "type": "checkbox" })
// .after allows us to add an element after, but maintain our chainability so that we can continue to work on the input
// here of course, I create a NEW label and then immidiatly add its "for" attribute to relate to our input ID
.after($("<label />").attr({ for: newID }))
// I should note, by changing your CSS and/or changing input to <button>, you can ELIMINATE the previous step all together
// Now that the new label is added, lets set our input to be a button,
.button({ text: false }) // of course, icon only
// finally, let's add that click function and move on!
// again, notice jQuery's chainability allows us no need to recall our element
.click(function(e) {
// FYI, there are about a dozen ways to achieve this, but for now, I'll stick with your example as it's not far from correct
var toConsole = $(this).button("option", {
icons: {
primary: $(this)[0].checked ? "ui-icon-check" : ""
}
});
console.log(toConsole, toConsole[0].checked);
});
// Finally, for sake of consoling this new button creation and showing you how it works, I'll return our ORIGINAL (yet now changed) element
return ele;
}
$(function() {
// This .each call upon the inputs containing the class I asiged them in the html,
// Allows an easy way to edit each input and maintain a counter variable
// Thus the "i" parameter
// You could also use your ORIGINAL HTML, just change $(".inp-checkbox") to $("input:[type='checkbox']") or even $("input:checkbox")
$(".inp-checkbox").each(function(i) {
// as previously noted, we asign this function to a variable in order to get the return and console log it for your future vision!
var newCheckBox = createCheckBox($(this), i);
console.log(newCheckBox);
});
// This next button added is simply to show how you can add new buttons at end-time
// ENJOY!!!
$("button").button().on("click", function(e) {
var checkBoxCount = $("#CheckBoxes .inp-checkbox").length;
var newCheckBox = $("<input />").addClass("inp-checkbox").appendTo($("#CheckBoxes"));
createCheckBox(newCheckBox , checkBoxCount);
console.log(newCheckBox);
});
});
Update: The original intent here was to purely answer the question, which was to create a jQuery UI styled checkbox and show how jQuery can be used in multiple ways. However, a later comment queried how to include a traditional style label with it. While there are a billion options for this, I'll simply take from the above and extend.
The first option I took is pretty simple. Using jsFiddle (without comments with UI theme switcher!), I made the following changes:
the JavaScript/jQuery
// First I add a new variable.
// It will simply be for a new "wrapper" element, in which to ensure our button is encased.
// Giving this element a new class gives easy access for later CSS or Event Triggering of sub elements (like the checkbox)
var newID = "cbx-"+i,
wrapper = $('<div />', { 'class': 'ui-checkbox-wrapper' }).appendTo('#CheckBoxes');
// Then I added a line to our ele series of methods.
// This line simply append our element (checkbox) to our new wrapper
// Take Note at how I added this method at start rather than at end.
// Had I not done this, then the label would not have been wrapped!
ele.appendTo(wrapper) // <-- new line!
.attr({ "id": newID })
Then I simply added the following CSS:
#CheckBoxes .ui-button .ui-button-text {
background: #A9A9A9;
display: inline;
font-size: 16px;
left: 19px;
padding: 0;
position: relative;
text-indent: 0;
top: -4px;
}
Results!
In Windows Forms there's a BeginUpdate/EndUpdate pair on some controls. I want something like that, but for jQuery.
I have a div, that holds a div. like this:
<div id='reportHolder' class='column'>
<div id='report'> </div>
</div>
Within the inner div, I add a bunch (7-12) of pairs of a and div elements, like this:
<h4><a>Heading1</a></h4>
<div> ...content here....</div>
The total size of the content, is maybe 200k. Each div just contains a fragment of HTML. Within it, there are numerous <span> elements, containing other html elements, and they nest, to maybe 5-8 levels deep. Nothing really extraordinary, I don't think. (UPDATE: using this answer, I learned there were 139423 elements in the fragment.)
After I add all the content,
I then create an accordion. like this:
$('#report').accordion({collapsible:true, active:false});
This all works fine.
The problem is, when I try to clear or remove the report div, it takes a looooooong time, and I get 3 or 4 popups asking "Do you want to stop running this script?"
I have tried several ways:
option 1:
$('#report').accordion('destroy');
$('#report').remove();
$("#reportHolder").html("<div id='report'> </div>");
option 2:
$('#report').accordion('destroy');
$('#report').html('');
$("#reportHolder").html("<div id='report'> </div>");
option 3:
$('#report').accordion('destroy');
$("#reportHolder").html("<div id='report'> </div>");
after getting a suggestion in the comment, I also tried:
option 4:
$('#report').accordion('destroy');
$('#report').empty();
$("#reportHolder").html("<div id='report'> </div>");
No matter what, it hangs for a long while.
The call to accordion('destroy') seems to not be the source of the delay. It's the erasure of the html content within the report div.
This is jQuery 1.3.2.
EDIT - fixed code typo.
ps: this happens on FF3.5 as well as IE8 .
Questions:
What is taking so long?
How can I remove content more quickly?
Addendum
I broke into the debugger in FF, during "option 4", and the stacktrace I see is:
data()
trigger()
triggerHandler()
add()
each()
each()
add()
empty()
each()
each()
(?)() // <<-- this is the call to empty()
ResetUi() // <<-- my code
onclick
I don't understand why add() is in the stack. I am removing content, not adding it. I'm afraid that in the context of the remove (all), jQuery does something naive. Like it grabs the html content, does the text replace to remove one html element, then calls .add() to put back what remains.
Is there a way to tell jQuery to NOT propagate events when removing HTML content from the dom?
In Windows Forms there's a BeginUpdate/EndUpdate pair on some controls. I want something like that, but for jQuery.
related:
jquery: fastest DOM insertion ?
You should try bypassing jQuery and empty out the content yourself after destroying the accordion:
$('#report')[0].innerHTML = '';
This will be much faster, but may cause IE to leak memory (jQuery goes to great pains to try to ensure that there are no references that prevent IE from doing garbage collection, which is part of why it is so slow to remove data).
I'm pretty sure the "why" will be revealed here (sourced from jQuery 1.4.2):
empty: function() {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
}
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}
return this;
},
cleanData: function( elems ) {
var data, id, cache = jQuery.cache,
special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
continue;
}
id = elem[ jQuery.expando ];
if ( id ) {
data = cache[ id ];
if ( data && data.events ) {
for ( var type in data.events ) {
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
} else {
removeEvent( elem, type, data.handle );
}
}
}
if ( deleteExpando ) {
delete elem[ jQuery.expando ];
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
}
delete cache[ id ];
}
}
}
Notice that the empty() function (and anything else that removes the DOM element) will call cleanData() which scans each contained node to remove the expando property and unbind the events that were bound, remove all the data that was in the internal cache, and what not.
You could perhaps solve this problem by splitting up the work a little. I'm not sure how many children you have in that element but you could try something like this:
$('#report').children().each(function() {
var $this = $(this);
setTimeout(function() { $this.remove(); }, 0);
});
Say there are 10 direct children of #report this will split the removing operation into 10 separate event loops, which might get around your long processing delay issue.