Is it possible to mix the concept of Unobtrusive JavaScript with the event model of ASP.Net?
ASP.NET makes it very difficult, as every server side control requires a postback via the __doPostback javascript function.
You can make sure you are not using any server side controls, but this means you loose most of the benefits of ASP.NET.
Another option is to override the OnRender event and output different controls/javascript, but this is also quite a lot of work and defeats the purpose of ASP.NET.
You have much greater control when using ASP.NET-MVC.
yes, to a point. discounting standard controls and how they are built, there's not much stopping your from offloading all your own javascript to a separate file. the big hangup that comes to mind is referencing myControl.ClientID to render an element id in the middle of a script block. with a little planning, you can still minimize the amount of script you have to render in the page to work around this.
I realize that this question has already been answered but for anybody surfing in, I somewhat disagree with the accepted answer.
It depends on what controls you are using.
Not all the controls require JavaScript. The biggest culprit for me was always LinkButton. A regular button control does not use JavaScript at all however. On my pages, I actually use regular buttons and use CSS and JavaScript to make them into LinkButtons. The first step is to use CSS to make them look like links. If you really want to get fancy, you can detach the button, add an HTML anchor, and associate all the event handlers for the button with the anchor. This means that a user without JavaScript sees a regular button (HTML input) that is styled with CSS. Anybody using JavaScript will see an HTML link (
Also, if you use JQuery, it is very easy to select ASP.NET elements without worrying about all the extra mumbo-jumbo that ASP.NET adds to the IDs.
Example:
<asp:Button id='theButton' text='Click here' cssclass='linkbutton' runat='server' />
You can select this individual button using JQuery:
var theButton = $("input[name$='theButton']");
You can also replace everything of class 'linkbutton' with HTML anchors:
$(function() {
var buttons = $(".linkbutton");
buttons.each(function() {
var button = $(this);
var id = button.attr('id');
/*
* If a link button is not working
* it is likely because it does not
* have an ID attribute - check that
*/
if (id)
{
var text = button.attr('value');
var cssclass = button.attr('class');
button
.before("<a id='"+id+"' class='"+cssclass+"' href=''>"+text+"</a>")
.hide()
.detach();
$("a[id='"+id+"']").live('click', function(event) {
$(this).after(button);
button.click();
event.preventDefault();
});
}
});
});
Things like GridViews are a bit more work but also doable. I found that, after the initial honeymoon, I avoided those kinds of controls and just used repeaters anyway. Repeaters do not impose any nasty JavaScript either.
Anyway, it is certainly possible to do unobtrusive JavaScript with ASP.NET WebForms.
Related
I am a Frontenddeveloper without further knowledge of C#, so please excuse my maybe stupid question. I am currently trying to use jQuery UI Dialog to replace the common JavaScript alert() and confirm() boxes. This is working good so far in the Frontend but now I found this snippet in one of the C# pages and I don't know what's the best way to switch this from confirm() to jQuery dialog(). Any ideas?
string delCode = "javascript:"
+ "if ( confirm(\'Are you sure you wish to delete this?\')){ "
+ this.Page.ClientScript.GetPostBackEventReference(this, string.Empty)
+ ";} else { return false; }";
EDIT:
After playing around a bit more I found out, that it will not work without adding an OnClientClick Attribute to prevent the Postback when clicking the button. How do I add this Attribute from within the Code behind?
btnDelete.Attributes.Add("OnClientClick","foo");
does not work for me. What's the right way to solve this?
Here ist what I want to have, it's just simple JS so far (you see the C# snippet for the Button as a comment above the HTML content in the Fiddle):
https://jsfiddle.net/SchweizerSchoggi/xts01hrx/16/
Sadly, to push JavaScript from server to client inside of Web-Form's you'll need to utilize the current code you have to implement. Often, to avoid the excessive Postback issue, I would force server side controls, like the LinkButton to disable the form submission.
<asp:LinkButton id="lbExample" runat="server" OnClientClick="launch(); return false;" Text="Launch Dialog" />
<script type="text/javascript">
function launch() {
$('[data-rel="Example-Modal"]').dialog({
// Dialog logic.
}
}
</script>
This way it would work more like a traditional JavaScript in a web-page, or single page application. The official documentation based on the code above, which is correct extra functionality can be found here. As you denoted above, your only option to dynamically add:
ClientScript.RegisterOnSubmitStatement(this.GetType(), "ConfirmSubmit", jQuery);
You would simply create the button progmatically or find the button in the page, from code behind. I'll use the create, to show you what I mean:
public void Page_Load(...)
{
var lb = new LinkButton();
lb.OnClientClick = "launch();"
}
I have a collection of AccordionPanes containing various TextBox controls and DropDownLists, each with their own validators.
If a few server-side validations occur on form submit, is there something that can automatically expand a previously minimized pane that contains the invalid validator message? Otherwise, it will seem to the user that the form isn't submittable without reason.
Another scenario: Let's say I have multiple panes with client side validators tied to the inputs. If a pane is minimized (and therefore you can't see the validator's ErrorMessage), is there a way to expand the appropriate pane when the AJAX page validation occurs on submit?
I know there's a brute-force way to this approach, where I keep track of every validator and their associated AccordionPane, but I was hoping for a better solution that can handle my situation for a large number of inputs/validators and panes.
How about something like this (using JQuery but I'm sure it can be converted into plain javascript)...
$(document).ready(function(){
if (isPostback()){
$.each(Page_Validators, function(index, validator) {
if (!validator.isvalid) {
// do something here to locate the accordion based on the validator
// $(this) is the currently invalid validator element as a jquery object/wrapped set
// so for example...
$(this).parent().slideDown();
// This assumes that the immediate parent of of the validator is the accordion which is unlikely but if you post your emitted html I can write the appropriate selector for you.
}
});
}
});
Because you dont want it to fire on initial load you can use a technique like this How to detect/track postback in javascript? and check if you are in a postback after the document.ready - I have assumed you've used the advice in the link and your function for postback detection is called isPostback().
there is a project built for this issue try to take a look at it....you can also download the source to analysis more details or use the same code-base if you want....http://www.codeproject.com/Articles/43397/Validating-Accordion-and-RadPanelBar-using-Ajax-an
Rich beat me to it, but here's the vanilla js version (ie9+):
Page_Validators
.filter(function(v) { return !v.isvalid; })
.forEach(function (v) { console.log(v.parentNode); });
Remember to place the code beneath the </form>-tag. I've had issues with using jQuerys document.ready and window.onload, since it might execute the code before all the needed JavaScript from asp.net is loaded.
Update: A more browser compatible version
for(var i = 0; i < Page_Validators.length; i++) {
var validator = Page_Validators[i];
if (!validator.isvalid) {
console.log(validator.parentNode);
}
}
I have a site that is in English and Spanish, and in each page of the site there is a link that leads to the Spanish version of that specific page, so if the user were on the "home.php" page, it would look like this:
<div id="language">
<ul class="language">
<li class="english"></li>
<li class="divider"></li>
<li class="spanish"></li>
</ul>
</div>
What I would like to do is leave the href and the class in the <a> tags in the HTML blank and assign a class and an href URL to the <a> depending on the page the user is on, that way I could, for example, just add that language div to an external file, and use an <include> to attach it to each page. To accomplish this I'm using the following code:
$('ul.menubar a').each(function(){
if(location.href.match('home.php')){
$('ul.language li.english a').addClass('active');
$('ul.language li.english a').append(function() {
$(this).attr('onclick', 'return false;');
});
$('ul.language li.spanish a').addClass('notactive');
$('ul.language a[href!="home.php"]').append(function() {
$(this).attr('href', 'inicio.php');
});
}
}
The problem is that the English version of the site has 4 links in the navigation bar (home.php, services.php, aboutus.php, contact.php), and the Spanish version likewise (with the corresponding translation of the URL names). I think that having to repeat that code 8 times (1 for each link, 4 links in each language) would be excessive and would actually add more code than simply adding the class and href url in the HTML. The point of using JS would be to simplify things.
So I basically would like to know if anyone can think of a better way to do this, that wouldn't require that much code. I'm trying to avoid having to, in the event that I'd need to change something, have to edit each different page. Also, I would like to know if this is the best way to achieve want I want to do using JavaScript.
HTML is best suited for managing content. CSS is best suited for presenting that content, and JavaScript is best suited for determining how that content behaves. Instead of trying to inject links and control the HTML from JavaScript; instead, leave the content where it belongs, inside the HTML, and use JavaScript to define one or two event-handlers to take action based on the class values on the hyperlinks themselves.
You already have a class on your English hyperlinks, and a separate class on your Spanish hyperlinks, so you can use this to your advantage.
Writing the Click Handlers:
Since toggling your "Language switch" most likely causes a boolean value to be set, you can use two click handlers to target all of your English links and all of your Spanish links, and then control the behavior based on the value of that switch at the time the links are clicked.
// handler for all English links
$('li.english a').click(function(event) {
event.preventDefault();
if(/* Switch is english */) {
window.location = $(this).attr("href");
}
});
// handler for all Spanish links
$('li.spanish a').click(function() {
event.preventDefault();
if(/* Switch is SPANISH */) {
window.location = $(this).attr("href");
}
});
Note that when a link is clicked, we first check the switch. Depending on it's value, we either redirect to that hyperlink, or simply prevent the default behavior -- going to a new page -- from completing.
Handling the Presentation:
Now, your other problem is going to be that, assuming your Spanish site and your English site are one in the same, you'll now see 8 hyperlinks in total. Again, this is where your switch can come in handy.
// single handedly hide or display the relevant content, based on the switch
function switchToEnglish() {
$('.english').show();
$('.spanish').hide();
}
function switchToSpanish() {
$('.spanish').show();
$('.english').hide();
}
Now, I don't know what else is contained in your switch function, but the general idea here is that we don't need to modify the content. We just need to show and hide the content. You'd need to integrate this concept into your existing switch function, if you don't already have something like this in place.
There are several advantages in this approach:
Your Web designers will still see href's in the HTML and can read and understand the HTML without needing your help or needing to go and look at JavaScript code. Not only will they see familiar patterns that they're used to seeing, but you'll likely have a better working relationship with them.
Search engines spidering your site will be able to read the links and follow them.
Browsers without JavaScript will be able to process the links. Some people seem to care about this. I don't. But it's worth mentioning anyway.
In summary, you're right about it being easier to manage in HTML. By using this technique, you can eliminate the repetition in the code that you're rightfully concerned about, and also move the content back to the HTML, as your gut is telling you is the correct thing to do. Not only will your code be more readable, but you'll get better SEO results as well.
I have written javascript function in my view page but it is giving me an error.I wnat to set the selected value of dropdown in the label.Please tell me where am I going wrong??
function OnSelect()
{
var label = document.getElementById("<%= lblSelection.ClientID %>");
label .Text= "You selected <b>";
}
Above is the script function
There is no text property on an element. Assuming the element refered to using label is something like a div or span, use:
function OnSelect()
{
var label = document.getElementById("<%= lblSelection.ClientID %>");
label.innerHTML = "You selected <b>";
}
Mixing Server and Client
Property Text is only available on the server Label control, but not on the client DOM element. You've mixed this a bit.
Avoid server controls in MVC
Even though you shouldn't use Asp.net web server controls with a MVC application. It's not recommended. They can be used but with caution. In your case where you don't distinguish between server and client controls I assume you're rather a beginner. No offence but I suggest you rather start and learn pure and clean MVC applications first before doing this kind of mix.
You seem to be confusing an asp:Label control on the server-side with a DOMElement on the client side. Take some time to learn the JavaScript environment and the DOM API. Then learn jQuery, which wraps the different DOM implementations with a very nice and consistent API.
Also When using MVC you shouldn't use WebForms controls unless you need to. This makes it much easier to understand what's going on in the client-side of things when your JavaScript is running.
As others have pointed out, Text is not a valid DOM property. You may be thinking of the text method that jQuery provides to set the innerHTML of an element as plain text.
However, you also end your string in with the bold tag, which leads me to believe that you plan on building out an HTML string with more content. If this is the case, you should use the following approach and build out the entire HTML string, and then update the DOM element.
var OnSelect = function() {
var html = "You selected <b>";
if ( someCondition ) {
html += "some value";
}
html += "</b>";
$("<%= lblSelection.ClientID %>").html(html);
};
On the other hand, if you mean to literally display You have selected <b>, and <b> is not supposed to be an HTML tag, then you'll need to do the following:
var OnSelect = function() {
$("<%= lblSelection.ClientID %>").text("You selected <b>");
};
Notice the use of text rather than html.
When ASP.NET controls are rendered their ids sometimes change, like if they are in a naming container. Button1 may actually have an id of ctl00_ContentMain_Button1 when it is rendered, for example.
I know that you can write your JavaScript as strings in your .cs file, get the control's clientID and inject the script into your page using clientscript, but is there a way that you can reference a control directly from JavaScript using ASP.NET Ajax?
I have found that writing a function to parse the dom recursively and find a control that CONTAINS the id that I want is unreliable, so I was looking for a best practice rather than a work-around.
This post by Dave Ward might have what you're looking for:
http://encosia.com/2007/08/08/robust-aspnet-control-referencing-in-javascript/
Excerpt from article:
Indeed there is. The better solution
is to use inline ASP.NET code to
inject the control’s ClientID
property:
$get('<%= TextBox1.ClientID %>')
Now the correct client element ID is
referenced, regardless of the
structure of the page and the nesting
level of the control. In my opinion,
the very slight performance cost of
this method is well worth it to make
your client scripting more resilient
to change.
And some sample code by Dave from the comment thread of that post:
<script>
alert('TextBox1 has a value of: ' + $get('<%= TextBox1.ClientID %>').value);
</script>
The comment thread to the article I linked above has some good discussion as well.
You can change to ClientIDMode property of the control to 'Static' that will result the same ID that you give the control in the .NET code.
<asp:TextBox ID="TextBox1" ClientIDMode="Static" runat="server"></asp:TextBox>
will result:
<input name="ctl00$MainContent$TextBox1" type="text" id="TextBox1">
so you have the same ID.
Couple of thoughts on this:
1) I've had a lot of luck getting elements by css class instead of id because asp.net ids are not reliable as you stated. I use this function and it performs reasonably well:
function getElementsByClass(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
{
node = document;
}
if ( tag == null )
{
tag = '*';
}
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
for (i = 0, j = 0; i < elsLen; i++)
{
if ( pattern.test(els[i].className) )
{
classElements[j] = els[i];
j++;
}
}
return classElements;
}
2) jQuery helps here alot. Using jQuery you can reliably get elements where the id ends with a certain string. While this is not "the" reason to use jQuery it's definitely a plus.
3) This will be fixed in asp.net 4.0 so hang in there :-) http://weblogs.asp.net/asptest/archive/2009/01/06/asp-net-4-0-clientid-overview.aspx
I prefer data bound tags in the markup document.getElementById('<%#TextBox1.ClientID %>').value, over the use of the server side tag implementation <% = TextBox1.ClientID %>.
Server side tags prohibit you from adding controls to the dom in the code behind. This need commonly arises as you build out your application and the databound approach may save you from major rewrites.
When using server side tags also know as 'code blocks' performing this common operation
this.Form.Controls.Add(myContorl);
generates this error at run time:
The Controls collection cannot be modified because the control
contains code blocks (i.e. <% ... %>).
Unfortunately this often only become inherently obvious after you have built out your web site.
When implementing data bound control '<%#TextBox1.ClientID %>' resolve the value of control properties referenced in the markup, in the appropriate place such as the end of Page_Load data bind like this:
Page.DataBind()
Keep in mind Page.DataBind() causes child controls on the page to also DataBind, this may be an unwanted side effect if the page handles the data binding of certain child controls separately. If this is the case, data binding can be performed on the individual control like this:
TextBox1.DataBind()
An applications evolution eventually leads to some sort of base site wide functionality where you may want to add base controls, once you've peppered you website application with server side tags replacing them with databinds becomes problematic, especially when pages have been coded to handle databinding on their own.
I don't think there's a single "best practice" for doing this. There's plenty of different pretty good practices. Here's ours:
Every control which has client-side functionality renders a script block inline, directly below the markup for the control:
<span id="something_crazy_long">
control markup
</span>
<script type="text/javascript">new CustomControl('something_crazy_long');</script>
Each control has an accompanying JS like:
var CustomControl = function(id) {
this.control = document.getElementByID(id);
this.init();
};
CustomControl.prototype.init = function() {
//do stuff to wire up relevant events
};
In the codebehind, we do something like:
class CustomControl : Control
override void Render(HtmlTextWriter writer)
{
writer.WriteBeginTag("span");
writer.WriteAttribute("id", this.ClientID);
writer.Write(HtmlTextWriter.TagRightChar);
//write control markup
writer.WriteEndTag("span");
writer.WriteBeginTag("script");
writer.WriteAttribute("type", "text/javascript");
writer.Write(HtmlTextWriter.TagRightChar);
writer.Write(
string.Format("new CustomControl('{0}');", this.ClientID)
);
writer.WriteEndTag("script");
}
I do something similar to Rex M except to avoid multiple script tags I use a function in my page base class to register the controls I am going to use clientside, then spit them out to html inside 1 script tag.
You could even subclass your controls to automatically register with the function or use a boolean property to set whether you are going to use them clientside.
For 'ctl00_ContentMain_Button1' - In asp.net when page renders in the browser, first part remains same 'ctl00'. Second part is ID of ContentPlaceHolder used 'ContentMain'. Third is ID of a control 'Button1'
I liked this http://codedotnets.blogspot.in/2012/01/how-get-id-server-control-javascript.html
Oh, and I also found this, in case anyone else is having this problem.
Use a custom jQuery selector for asp.net controls:
http://john-sheehan.com/blog/custom-jquery-selector-for-aspnet-webforms/
You can get the ID by using document.getElementById method as well.