Structure for referencing HTML elements from Javascript in MVC3 - javascript

We have an ASP.NET MVC3 project. We populate several parts of our site using ajax calls. Lets say we have a div in our cshtml where we want to put some search results.
<div id="SearchResult" />
We have created a list with constants for all elements that we want to refer in our Javascript files
var selectors = {
SearchResult: '#SearchResult',
...
};
When we want access the div we use selectors.SearchResult in our Javascripts.
So far, so good. But coming from a c# world where we have strong bindings, compile time warnings, etc, we think the coupling here is a little bit on the loose side. If we want to refactor our div ids we have to be very careful to find all references and update them.
Are there any good practices for keeping javascript and HTML identifiers in sync? Any templates/scripts for extracting all div ids into a javascript file?

Add a script to the view
<script type="text/javascript">
window.viewConfig = { searchResult: '#SearchResult' };
</script>
and have your JS files read this window.viewConfig.searchResult.
You could also take this further and store the id in a variable and use it.
#{
var myId = "SearchResult";
}
then use #myId so the div would be
<div id="#myId"></div>
and the script would be
<script type="text/javascript">
window.viewConfig = { searchResult: '##myid' };
</script>

Related

Is it possible to do some client-side DOM manipulation before document ready?

I am trying to find a way (if possible) to use javascript to add some attributes to an element at render time and before the DOM is fully loaded. I know, that sounds counterproductive, but let me give you some background:
I'm working in an extremely limited templating platform that gives me access to some page variables, but they need some minor string manipulation. I can't leverage any of the ASP preprocessing so it has to happen on the client-side.
Specifically, I am trying to add Schema.org Microdata markup to an element before Googlebot scans through the document contents.
Essentially I need to modify an attribute value from $5.99 to 5.99.
Here's my most recent attempt, which makes the DOM modifications correctly, but not before Google's rich snippet validator processes the page:
<div class="pitinfo"><div class="padleft padright"><%Product.BasePrice%></div></div>
<!-- at page bottom -->
<script type="text/javascript">
(function() {
var pricesting = "<%Product.BasePrice%>";
var price = pricesting.slice(1);
$('.pitinfo').attr('itemprop', 'price');
$('.pitinfo').attr('content', price);
})();
</script>
After load I get this <div class="pitinfo" itemprop="price" content="9.99">$9.99</div>, however the Rich Snippet Testing tool tells me price is not set.
I have already tried using ASP in my template code but the hosting provider does not allow it.
Is it possible to make the DOM modifications sometime in the middle of the document rendering flow?
It is possible to insert a <script> tag inside of the <body>. JavaScript inside of the tag is loaded before the HTML after it, so you would be able to edit the element's value before the rest of the HTML/JS is loaded.
For example:
<div>
<div id="element" value="$5.99"></div>
<script>
var element = document.getElementById("element")
element.value = 5.99;
</script>
</div>
You can check it out here

Ways to pass information from PHP to JQUERY?

I'm having a hard time figuring out what is the best way to transfer information from PHP to Jquery. I know putting PHP in Javascript is a really bad alternative (my JS and PHP files are seperated anyway), so what I usually do is print the info with PHP in the HTML with a input type hidden and retrieve the info in JQuery using the element.text() or .val() or any other method, but I feel this is kind of noobish. Is there a better way?
I recommend never using a server-side language to build JavaScript. Instead, keep your data in HTML (model), your styles in CSS (view), and your interactions in JS (controller).
When you need to pass data to JavaScript, make good use of attributes and templates.
When it comes to passing data via attributes, you can go a long way with just using native attributes.
In this example, it is quite apparent that the link will open a modal, and the [href] attribute is used to point to where the modal lives:
Open modal
<div id="modal">lorem ipsum</div>
When you can't fit data within native attributes, you should make use of [data-*] attributes.
In this example, [data-tooltip] is being used to store a rich-text tooltip.
<div data-tooltip="<p>lorem ipsum</p>">...</div>
Even more importantly, jQuery automatically grabs and casts all [data-*] attributes on a DOM node when .data() is called on the node.
<div id="example"
data-foo="bar"
data-fizz="true"
data-max="100"
data-options="{"lorem":"ipsum"}">
...
</div>
<script>
$('#example').data();
/*
{
foo: 'bar',
fizz: true,
max: 100
options: {
lorem: 'ipsum'
}
*/
</script>
The thing to watch out for is JSON encoding within HTML encoding. jQuery makes it very straight-forward to get the data out of the DOM node without needing to worry about decoding, but in PHP you need to make sure that you json_encode data before calling htmlentities:
<div data-example="<?= htmlentities(json_encode($someArray)) ?>">...</div>
In some instances you may want to make use of jQuery's .attr() method.
For large amounts of data, such as an HTML template string, you can use <script> tags with a template mime type (such as type="text/x-jquery-tmpl"):
<script id="template-example" type="text/x-jquery-tmpl">
<h1>${title}</h1>
<p>${body}</p>
</script>
Or you can use the HTML5 <template> element:
<template id="template-example">
<h1>${title}</h1>
<p>${body}</p>
</template>
You can then access the HTML contents of the element run it through whatever your favorite flavor of string templating happens to be.
You can simply echo PHP variables into Javascript variables for your jQuery to use like this:
<?php
$stringVar = "hi";
$numVar = 1.5;
?>
<script>
var stringVar = "<?php echo $stringVar; ?>";
var numVar = <?php echo $numVar; ?>;
</script>
This can be used for practically any data type as long as you convert it correctly upon echoing it (oftentimes json_encode() is enough for complex structures such as arrays and objects).
Also note that this way will only work if your jQuery code is included after these variables have been defined.
The other way is to look into using AJAX, but if these are static variables that don't need to change within the lifetime of the page then I would go with the method above.

ASP.net MVC - Views and jQuery Best Practices

I'm trying to figure out what the best practice is for using jQuery in an MVC app. Specifically, I would like to know what I should do so that I don't clutter all my views with individual document.ready statements.
As an example:
I have the following Views:
/Views/Shared/_Layout.cshtml
/Views/Home/Index.cshtml
/Views/Home/_Dialog.cshtml
/Views/Home/_AnotherDialog.cshtml
I have a controller action that will render the Home/Index View, which uses the Layout and renders two partial views (or editor templates, display templates, etc.). This one controller action has rendered 4 or more views. Each view is using some jquery document.ready code.
Currently, I have the code at the bottom of each view:
// In Index
<script type="text/javascript">
$(function() {
$('#tabs').tabs()
});
</script>
// In _Dialog
<script type="text/javascript">
$(function() {
$('#some-dialog').dialog( ... );
});
</script>
I know this isn't a very good practice because it is already getting unmanageable in my small project. What are some good practices to follow when I have tons of pages that all need some jQuery / javascript initialization code separated across dozens of views?
You could do something along the lines of what Telerik do with their javascript registrar. Basically, make this registrar available in your view model. At the simplest level, all it has to do is keep track of strings added to it:
public class JavascriptRegistrar
{
private StringBuilder jsBuilder_ = new StringBuilder();
public Add(string js)
{
builder.Append(js).Append('\n');
}
public string ToString()
{
return "<script type=\"text/javascript\">" + jsBuilder_.ToString() + "\n</script>";
}
}
Your partial views will then add to this when rendering:
<h1>In my view!</h1>
#Model.Registrar.Add("$function() { /* ... */ }")
Finally, at the bottom of your main view, when you're done:
#Model.Registrar.ToString()
Which will write out all the javascript it has collected during rendering.
If the initialisation is specific to a view and you know it definitely won't be used outside that view, for example some page specific behaviour, then just leave it in the view!
There is nothing wrong with having script tags in all your views, as long as you aren't replicating js between views. I think people tend to misunderstand 'separation of concerns' in this case and think that simply means 'keep different languages away from each other at all costs'...that is wrong, clearly if some page initialisation logic/behaviour is specific to a page, then the html and js intrinsically 'concern' each other, therefore moving the js into a separate file is not really 'good practice', if anything it makes your code more difficult to understand.
I personally like to open up a View, and be able to see all the js and css that is specific to that page as soon as I open it, makes it nice and readable. However, obviously if code needs to be shared then you need to bust it out your view and get in your scripts folder whwere it can be referenced by anything!
EDIT
In your example above I see in your Index view you initialise your tabs. This is fine as it is, however, if you added tabs somewhere else in the project then it might be better to create your tabs using a .tabs class rather than #tabs id, and then in an external js file initialise all your tabs at once by calling $('.tabs').

how can I detect page when I compile all javascript to one file

I compile all my javascript for different pages into one file, so I have to identify page for my all.js. I can put a hidden element in my pages and let javascript detect this element, but I don't like this solution, are there any other ways to do this?
You could go by the url using location.href (or another field from the location object).
However, a better approach is using a data- attribute on the body tag, e.g. <body data-page="whatever"> and then using $('body').data('page') to retrieve the value.
If you script is based on pages, then compiling them into one script is a bad idea, load the file separately, it will be lighter and definately increase some performace.
I am not sure, why do you need this, but in general it is not good practice to change dynamicaly change content of javascript file, since you are disabling javascript cacheing, what can be performance issue later.
Any way, you can solve it from other side, what about using all.js just to detect the page, where are you and then you can use this information, to load right javascript file dynamicaly, like in the following example
document.write('<script src="'+location.pathname+'.js"></script>');
Which will load same file as you are on, just with .js extension. So for example on index.html page it will load index.html.js file
I almost always use MVC frameworks and tend to put my action and controller as classes on the body element
<body class="main_controller index">
Which lets you do things like this:
$(document).ready(function(){
//Only for lessons#search
if (!$(body).hasClass('lessons search')) {
return;
}
function close_style_filter_box() {
$('#style_filter_box').slideUp();
}
});
$(document).ready(function(){
//Only for main_controller#index
if (!$(body).hasClass('main_controller index')) {
return;
}
function do_something_else_on_this_age() {
....
}
});
Another way is using javascript variable:
var PAGE = 'page1';

How to embed HTML via JS embed code

I need to give the user a snippet of js code that will insert some HTML code into the page.
I'm wondering what the best method to do so is. Should I use document.write, should I just create all the HTML elements via DOM programmatically?
Is it possible to use a js library? I can see conflicts occurring if the webpage the code is embedded in already contains the library.
Using a library is probably too heavyweight, inserting DOM elements is very verbose and document.write may not work if the target site uses the application/xhtml+xml content type. I think your best bet is to construct one element using document.createElement and then setting innerHTML on that.
A suggestion:
Insert this DIV wherever you want the output to appear:
<div id="uniqueTargetID" style="display: none;"></div>
Then at bottom of page have this:
<script src="snippet.js"></script>
This file (remotely hosted or otherwise) contains could output simple text this way:
var html = [];
html.push('<h1>This is a title</h1>');
html.push('<p>So then she said, thats not a monkey, its a truck!</p>');
html.push('<p>You shoulda seen his face...</p>');
var target = document.getElementById('uniqueTargetID');
target.innerHTML = html.join('');
target.style.display = 'block';
I would avoid using document.write() if you can help it.
Javascript::
//to avoid global bashing
(function(){
var target = document.getElementById('ScriptName'),
parent = target.parentElement,
oput = document.createElement('div');
oput.innerHTML = "<p>Some Content</p>";
parent.insertBefore(oput, target);
}());
HTML to give to client/people::
<script type="text/javascript" id="ScriptName" src="/path/to/ScriptName.js"><script>
ScriptName should be something unique to your script.
If its simple insertion you can use pure js, otherwise if you want to provide some complex functionality you can use library. The best choice in this case will be the lib that does not extend root objects (Array, Function, String) to prevent conflicts (jQuery in noConflict mode, YUI, etc.).
Anyway it will be better to avoid using document.write u'd better use setting of innerHTML of existing element or create new one.

Categories