Kentico NodeName as a body class? - asp.net

I'm trying to identify the home page, and then sub-pages on nodes. Ideally as a class on the Body so i can make style changes based on which section the user is in.
I have two .master pages, Global and Site. The body tag is in Global, and Site is a child master.
As this is an English/French site, I'm hoping using the Node Name would be the easiest approach for me.

If your home page and internal pages are of different Page Type then you can benefit from the macro below. We use something like this to give Page type specific id on the body tag and is usually helpful
<body class="{% CurrentDocument.NodeClass.ClassName.ToString() #%}">
However, it can be tweaked to suit your needs.

I'd recommend you to implement one generic stylesheet, shared across all the pages, and a couple of section specific stylesheets.

I'm not 100% clear about what you are describing, but you could use NodeLevel direct as / is NodeLevel = 0, and everything else will be Level 1+

Here's what i've come up in the .cs of my global .Master. So far it seem to be giving me enough to work with. From here i can use js and css to target what i need.
string aliasPath = CMS.DocumentEngine.DocumentContext.OriginalAliasPath.ToLower().TrimStart('/');
if (aliasPath == "")
{
this.BodyClass += " home";
}
else
{
this.BodyClass += " " + aliasPath.Replace("/","_");
}

The simplest approach, if you are willing to add either a data attribute or an id attribute to the body tag, is to add something like this to the master page, in the text box of the body tag:
id="{%nodealias%}"
Since the node alias will typically be unique unless you have multiple pages, at different paths, with the same name, ID should work fine. I prefer to use "nodealias" as opposed to the friendly name because it ensures there will not be any special characters, and will replace white space etc with a dash "-". The above macro will add the node alias as an id attribute to the body tag. Here's a screenshot:
Alternative approach explained here: https://devnet.kentico.com/forums/f49/t43559/body-class which involves setting the body class using a combination of macros and code behind, or via a custom web part. Basically you can modify it using CMS.CMSHelper.CMSContext.CurrentBodyClass (this was written in 2014 so the syntax will be slightly different)
-Edit:
Adding to some of the other answers, if you'd rather add a stylesheet on a template or page level, you can do so. To add a stylesheet reference on every instance of a template, edit the template header properties:
If you want to include the stylesheet on any particular page, this can be accomplished by editing the General tab on the page level, but this will remove the main stylesheet you are using (depending on how you are including it in the page):

In the body section in the Master page you can start with
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if(CurrentDocument.DocumentName== "Site")
CMS.DocumentEngine.DocumentContext.CurrentBodyClass += "body-site";
else
CMS.DocumentEngine.DocumentContext.CurrentBodyClass += "body-global";
}
</script>

Related

How do I access a DIV from javascript, if ASP.NET mangles its ID?

I have a web page that contains a "div" element. On the page, there is javascript to reference the div: document.getElementById('divId'). This was working fine until another developer redesigned the page to use an ASP master page.
Now, document.getElementById('divId') returns null. It appears that ASP.net prepends some characters to the names of elements within contents forms when you use a master page. How can I know what the id of the div is when the page loads?
Update Allow me to give a specific example to clarify the question: My page had a div with ID divNotice. After changing my page to use a master page, I see when I print the source to the page that renders that the div ID is ctl00_ContentPlaceHolder1_divNotice. My question is, how am I supposed to know what the div ID is going to be when the framework is done with it?
I think that this is what you looking for.
document.getElementById('<%=divNotice.ClientID%>')
to get the ID of your element as appears on the html page use .ClientID
Hope this help.
Dynamically create the javascript using Control.ClientID to determine the calculated ID of div.
document.getElementById('<%= DivControl.ClientID %>')
Or search for the element on the client side using the base ID as a search pattern. See here: A generic way to find ASP.NET ClientIDs with jQuery
I prefer the server side calculation, but if you don't do it often and/or your current design prohibits it, the client side way is a reasonable workaround.
you can check i the element exists by checking if it returns not null
if (document.getElementById('divId') != null) { /* do your stuff*/ }
in other words:
if (document.getElementById('divId')) { /* do your stuff*/ }
now you have edited you orginal question i got it.. i would do something like this:
var arrDivs = document.getElementsByTagName('div'),
strDivName = "divId";
for (i=0;i<=arrDivs.length;i++){
if( arrDivs[i].id.indexOf(strDivName) != -1) {
alert("this is it")
}
}
you can see a demo here:
http://jsfiddle.net/pnHSw/2/
i think you could do it better with a regex.
But this is a pure JS way i don't know ASP.net
edit: i think Aristos solution is much cleaner :P
maybe you can use a descendent selector un css
<div id="wrapperControler">
<controler id="controler"></controler>
</div>
wrapperControler controler{
dosomething;
}

Dynamically adding CSS cuts down page weight?

Ok, this is bizaare and the only shop I've seen do this..granted we only have 2 developers in this shop. My boss thinks that adding css files individually to pages using a helper method makes our site more efficient because when adding the style references in the Masterpage, then every page gets those styles making the site load slower.
So we have a company.css which is our main styles for stuff that is global like the main buckets such as header, footer, main, etc.
Then he wants me to go into each code-behind of lets say a certain set of pages for one of our features and use a utility method we created to dynamically add the product-related style sheet to those pages. So for instance lets say we have a Reviews & Ratings feature. We put all our reviews & ratings related classes and ids in Reviews.css. Ok great.
So for instance in the .aspx.cs pages Init, we'd put this:
Util.AddStylesheet("Ratings.css");
Now he wants me to include that stylesheet in the codebehind of those pages where our reviews code touches in .aspx pages. But says do not put a reference to Reviews.css in the master page.
Am I the only one who questions this method and says what's the point of having a master page then? I mean really, if you put all your .css references in your master page does the argument of saying that now all pages have all these styles (some not used because they do not relate to that page) add weight to your load time for end users?
I mean common right? Maybe people do this but I've never seen it and it's a management nightmare cause you have references all over the fing place dynamically adding certain .css pages to subsets of .aspx pages.
To me, even though we are an e-commerce site that receives 1 million hits a month, please, I mean seriously adding stylesheets manually and bypassing just putting them all in a master page just boggles my mind. Am I the only one who thinks this is nuts and questions his assumption that doing that would make page weight excessive because they would be inheriting all stylesheets, some which would be wasted .css because it doesn't necessarily relate to that particular page? For me, adding them to the master page is a done deal...simple, maintainable, and who cares.
I can understand page weight with JS, but .css?
What you actually want to do is minimize the number of total http requests made, as well as the total bandwidth used. If you have your reviews.css file linked from the master page, you need an extra http request for every page on your site, whether not you ever use the styles. So in that sense, your boss is right. Don't put this on the master page if it won't be used from all or most of your pages.
That said, I wouldn't have anything in the code-behind for this either, but rather a user control you can put on the required pages to render a style section or link.
I actually do do the compromise fairly often.
Most pages in my sites use all the standard css files declared in the masterpage but there's the odd one page here and there that use a special stylesheet. A high resolution view, a print view or maybe a jquery lighbox/gallery type control.
My ad-hoc stylesheet loading code is as follows:
using System;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
namespace ExtensionMethods
{
public static class PageExtensions
{
public static void AddStyleSheet(this Page page, String Href)
{
PageExtensions.AddStyleSheet(page, Href, "all");
}
public static void AddStyleSheet(this Page page, String Href, String Media)
{
Href = VirtualPathUtility.ToAppRelative(Href);
if (!PageExtensions.StyleSheetAlreadyExists(page, Href))
{
HtmlLink htmlLink = new HtmlLink();
htmlLink.Href = Href;
htmlLink.Attributes.Add("rel", "stylesheet");
htmlLink.Attributes.Add("type", "text/css");
htmlLink.Attributes.Add("media", Media);
page.Header.Controls.Add(htmlLink);
}
}
static private bool StyleSheetAlreadyExists(Page page, String Href)
{
var preExisting = from Control c in page.Header.Controls
where c is HtmlLink && ((HtmlLink)c).Attributes["Href"] == Href
select c;
return preExisting.Any();
}
}
}
Remember, by default CSS files are being CACHED in the browser, so CSS will be loaded only ONCE anyway.
From my experience more seperate CSS files == more duplicated rules == bigger mess.
If you want to save up bit of bandwidth make sure you compress your CSS when writing it, so instead of:
#review
{
margin-top:10px;
margin-left:12px;
margin-bottom:12px;
margin-right:35px;
background-color:#ffffff;
background-image:url(images/blah.png);
background-repeat: repeat-x;
}
You write:
#review{margin:10px 35px 12px 12px;background:#fff url(images/blah.png) no-repeat;}
How about suggesting a compromise?
One master page with the most-commonly used elements defined (headers, links, navigation lists, etc) and then a location-specific stylesheet, ie catalogue.css, for such pages as need those styles. So the css is still able to be cached, but isn't forced on everyone that just visits the about-us page.
It's not ideal, but if the goal is to reduce the bandwidth of a single monolithic css file, it might get you some headway.
As it is, my personal approach is always to use the monolithic stylesheet; though having thought about it I'm tempted to try the suggested approach. It should work? Maybe... =/

ASP.NET - Conditionally link CSS file

I want to conditionally link a CSS file base on the user's Membership role. An administrator should link my admin.css file while everybody else (other roles and anonymous users) should display my global.css file. Can this be done?
Try this:
protected void Page_Init(object sender, EventArgs e)
{
HtmlLink css = new HtmlLink();
// add conditional logic to add correct css file
css.Href = "css/fancyforms.css";
css.Attributes["rel"] = "stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["media"] = "all";
Page.Header.Controls.Add(css);
}
Either you can create theme based on the role - AdminTheme (will contain admin.css) & GlobalTheme (will contain global.css), or else you can dynamically write the <link> element in the <head> tag after giving the runat="Server" attribute to it.
You can then set the page's theme dynamically in the PreInit or Init event based on the role.
Sure, when you're outputting the <head> section, have an if statement check a session variable that has their "user level", then output the corresponding <link> tag (or #import line, depending how you prefer to do your CSS).
If you set the head element of your page to run server-side, you can inject a new HtmlGenericControl into the header that represents the link if the user is in a particular role.
If you wish to enable/disable/show/hide controls based on the role this would be nothing more than "security by obscurity" since switching off styles or setting a browser to a specific css file to override what is actually served would easily display all that is secret.
Another issue would be caching. Some browsers like Opera/Firefox happily cache all that is cacheable, so the user will have to click "Reload page". You can probably disable caching but then your css will be downloaded all over again thus unnecessarily consuming traffic.

How can you move ASP.Net controls to different places on the Web form at runtime?

Is there an accepted way to "move" a control.
My client wants to place a certain chunk of markup (representing some visual element) in one of several different places on the page. The locations are different to the point that I can't effect the change on CSS along (by floating it or something).
I considered just putting the control in multiple spots with Visible set to "false," then displaying the one in the place they wanted for that particular page.
However, the code for this control is not trivial -- there's a couple template sections, for instance. Having to dupe this in multiple places would get unwieldy. Also, I don't want to have to work with this control strictly from the code-behind for the same reason.
So, I'd like to put it in one place on the Web form, the move it around based on where I want it. Could I put Placeholders in different spots, have the control in one spot, then remove and add it to the right spot? I suspect this would work.
Does someone have a better idea? Is there a best practice for this?
I'd recommend using a placeholder control, moving your markup into a separate user control, then loading this at runtime and adding it to the relevant placeholder.
Eg.
// Load a user control
MyControl userCtrl = (MyControl) LoadControl("~/Controls/MyControl.ascx");
// Or create an instance of your control
SubclassedControl subclassedCtrl = new SubclassedControl();
// Do stuff with controls here
userCtrl.LoadData();
subclassedCtrl.Text = "Hello World";
// Check which placeholder to add controls to
PlaceHolder placeHolder = (foo=="bar") ? placeHolder1 : placeHolder2;
// Add the controls
placeHolder.Controls.Add(userCtrl);
placeHolder.Controls.Add(subclassedCtrl);
This will avoid cluttering up your page with unnecessary markup, and loading it at runtime will also avoid unnecessary confusion later, when another developer looks at the code and can't immediately see why a control is in one place in the markup, but renders on a completely different part of the page.
An alternative (and one I've seen done many times before) is through javascript and the DOM. Render your control inside a hidden div tag. So you would render your content here:
<div id='rendercontent' style='display:none'>
.. control here ..
</div>
Then, lets say you wanted to move it all here (the span tag is inside because that's what we're going to replace):
<div id='newlocation1'><span></span></div>
You would define the following javascript:
<script language="JavaScript">
function replaceNode(newElementID, targetElementID)
{
var targetElement=document.getElementById(targetElementID);
var newElement=document.getElementById(newElementID);
targetElement.replaceChild(newElement, targetElement.firstChild);
}
</script>
And when you want to move the content to the new location, call:
<script language="JavaScript">
replaceNode('rendercontent','newlocation1');
</script>
Do Web Parts do what you want to do?
Or, you can change the parent programmatically of your controls to move them into a separate area.
You can override the Render method and place the controls wherever you want in the html.
You only need to add controls to the Controls collection that must interact on the server. The rest of your HTML can just be written to the response stream. If you override Render you can create the html anyway you see fit, placing the controls in any order.
Below is an example of how to write out your html.
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.RenderBeginTag(TagKey);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
_control.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
}
You could always put panels in the pre-defined locations and add the control to the specific panel at runtime.. Here's an example adding a label (the label could be replaced with any control).
Dim lblDisplay As Label = New Label()
lblDisplay.ID = "myLabel"
lblDisplay.Text = "Some Text"
pnlDisplay.Controls.Add(lblDisplay)
As far as...
"Also, I don't want to have to work
with this control strictly from the
code-behind for the same reason."
I think you're going to have to do most of your work in the code behind.
PS.. a good example of the whole usercontrol setup can be downloaded here..
http://www.asp.net/downloads/starter-kits/time-tracker/

How can I use a traditional HTML id attribute with an ASP.net runat='server' tag?

I am refactoring some CSS on a website. I have been working on, and noticed the absence of traditional HTML IDs in the code.
There is heavy use of CssClass='…', or sometimes just class='…', but I can't seem to find a way to say id='…' and not have it swapped out by the server.
Here is an example:
<span id='position_title' runat='server'>Manager</span>
When the response comes back from the server, I get:
<span id='$aspnet$crap$here$position_title'>Manager</span>
Any help here?
Use jQuery to select the element:
$("span[id$='position_title']")....
jQuery's flexible selectors, especially its 'begins with'/'ends with selectors' (the 'end with' selector is shown above, provide a great way around ASP.NET's dom id munge.
rp
The 'crap' placed in front of the id is related to the container(s) of the control and there is no way (as far as I know) to prevent this behavior, other than not putting it in any container.
If you need to refer to the id in script, you can use the ClientID of the control, like so:
<script type="text/javascript">
var theSpan = document.getElementById('<%= position_title.ClientID %>');
</script>
Most of the fixes suggested her are overkill for a very simple problem. Just have separate divs and spans that you target with CSS. Don't target the ASP.NET controls directly if you want to use IDs.
<span id="FooContainer">
<span runat="server" id="Foo" >
......
<span>
</span>
You can embed your CSS within the page, sprinkled with some server tags to overcome the problem. At runtime the code blocks will be replaced with the ASP.NET generated IDs.
For example:
[style type="text/css"]
#<%= AspNetId.ClientID %> {
... styles go here...
}
[/style]
[script type="text/javascript"]
document.getElementById("<%= AspNetId.ClientID %>");
[/script]
You could go a bit further and have some code files that generate CSS too, if you wanted to have your CSS contained within a separate file.
Also, I may be jumping the gun a bit here, but you could use the ASP.NET MVC stuff (not yet officially released as of this writing) which gets away from the Web Forms and gives you total control over the markup generated.
Ok, I guess the jury is out on this one.
#leddt, I already knew that the 'crap' was the containers surrounding it, but I thought maybe Microsoft would have left a backdoor to leave the ID alone. Regenerating CSS files on every use by including ClientIDs would be a horrible idea.
I'm either left with using classes everywhere, or some garbled looking IDs hardcoded in the css.
#Matt Dawdy: There are some great uses for IDs in CSS, primarily when you want to style an element that you know only appears once in either the website or a page, such as a logout button or masthead.
The best thing to do here is give it a unique class name.
You're likely going to have to remove the runat="server" from the span and then place a within the span so you can stylize the span and still have the dynamic internal content.
Not an elegant or easy solution (and it requires a recompile), but it works.
.Net will always replace your id values with some mangled (every so slightly predictable, but still don't count on it) value. Do you really NEED to have that id runat=server? If you don't put in runat=server, then it won't mangle it...
ADDED:
Like leddt said, you can reference the span (or any runat=server with an id) by using ClientID, but I don't think that works in CSS.
But I think that you have a larger problem if your CSS is using ID based selectors. You can't re-use an ID. You can't have multiple items on the same page with the same ID. .Net will complain about that.
So, with that in mind, is your job of refactoring the CSS getting to be a bit larger in scope?
I don't know of a way to stop .NET from mangling the ID, but I can think of a couple ways to work around it:
1 - Nest spans, one with runat="server", one without:
<style type="text/css">
#position_title { // Whatever
}
<span id="position_titleserver" runat="server"><span id="position_title">Manager</span></span>
2 - As Joel Coehoorn suggested, use a unique class name instead. Already using the class for something? Doesn't matter, you can use more than 1! This...
<style type="text/css">
.position_title { font-weight: bold; }
.foo { color: red; }
.bar { font-style: italic; }
</style>
<span id="thiswillbemangled" class="foo bar position_title" runat="server">Manager</span>
...will display this:
Manager
3 - Write a Javascript function to fix the IDs after the page loads
function fixIds()
{
var tagList = document.getElementsByTagName("*");
for(var i=0;i<tagList.length;i++)
{
if(tagList[i].id)
{
if(tagList[i].id.indexOf('$') > -1)
{
var tempArray = tagList[i].id.split("$");
tagList[i].id = tempArray[tempArray.length - 1];
}
}
}
}
If you're fearing classitus, try using an id on a parent or child selector that contains the element that you wish to style. This parent element should NOT have the runat server applied. Simply put, it's a good idea to plan your structural containers to not run code behind (ie. no runat), that way you can access major portions of your application/site using non-altered IDs. If it's too late to do so, add a wrapper div/span or use the class solution as mentioned.
Is there a particular reason that you want the controls to be runat="server"?
If so, I second the use of < asp : Literal > . . .
It should do the job for you as you will still be able to edit the data in code behind.
I usually make my own control that extends WebControl or HtmlGenericControl, and I override ClientID - returning the ID property instead of the generated ClientID. This will cause any transformation that .NET does to the ClientID because of naming containers to be reverted back to the original id that you specified in tag markup. This is great if you are using client side libraries like jQuery and need predictable unique ids, but tough if you rely on viewstate for anything server-side.
If you are accessing the span or whatever tag is giving you problems from the C# or VB code behind, then the runat="server" has to remain and you should use instead <span class="some_class" id="someID">. If you are not accessing the tag in the code behind, then remove the runat="server".

Resources