Best practice to set up JavaScript event handlers for partial views - javascript

I have a partial view that is loaded via JQuery ajax. It enumerates through properties in the Model and renders HTML elements which need JavaScript events bound.
Here is some simplified razor code I have now
foreach(MyObject item Model.MyObjectList)
{
string containerId = "Container" + item.Id;
string onMouseOut = "DoSomething('"+containerId+"',#Model.Id)";
<div id="#containerId" onmouseout="#onMouseOut">
//Other code here
</div>
}
This works OK however it is often said to be better to bind events in JQuery, if I did this you could also utilise JQuery events such as "onmouseleave".
So another method I could do is place a script block inside each enumeration which sets up the events like so
<script type='text/javascript'>
$('##containerId').mouseout(function(){
DoSomething('#containerId',#Model.Id)
});
</script>
However this results in lots of script block being rendered.
Is there another better solution for setting up events in partial views?

Use the .on method to bind events to things that will not always be inside the DOM.
$('#foo').on('click', function() {
// do stuff
});
You then remove the need to insert script tags in your partials. There was a method similar to this that was .live() but has since been deprecated in v1.7 and removed since v1.9.
jQuery API : .on();

Related

How can I dynamically change css for html that is generated by javascript at runtime?

I have some html that is generated programmatically using javascript at runtime.
I want to be able to dynamically change the css properties of this html
e.g.
$(".pointsbox").css("background-color","green");
but it appears to not work as those html elements are not available at the time that the css change is called.
I am pretty sure I have managed to do this before but I've forgotten what the function is called.
Any help much appreciated!
You haven't posted code on how exactly you create your HTML elements, but it can be something as simple as this:
You can create an HTML element by passing HTML into the jQuery function right?
var new_element = $('<div>');
Well, you can treat that like any other jQuery object, and just manipulate its CSS right then and there.
var new_element = $('<div>').css('background-color', 'green');
Heck, you can even chain the create, the css change and the DOM insert in one call.
var new_element = $('<div>')
.css('background-color', 'green')
.appendTo('#container')
;
There are the Mutation events - specifically the DOMNodeInserted event - that you could bind an event handler to. However, as the page I linked states, it's recommended that you don't because it has a serious negative effect on the performance of your page and the cross-browser support isn't particularly good.
An alternative is to simulate your own DOMNodeInserted event using a custom event. Essentially you bind a handler for a custom event (say nodeinserted) on the document, then trigger that event whenever you have code that dynamically modifies the structure of your page. Code might look something like the following:
$(document).on('nodeinserted', function() {
$('.pointsbox').css('background-color', 'green');
});
function modifyPage() {
// code to modify your page here
$(document).trigger('nodeinserted');
}
Note that, with this approach, you'll need to modify all functions that add elements to the page to trigger that nodeinserted custom event.
I use this. It ensures the DOM has loaded.
$(document).ready(function(){
//code here
$(".pointsbox").css("background-color","green");
});

Javascript: Replace string in html without destroying the elements for future event handlers

I have this code that searches for a given string of characters (_aff.tractionsCode) and replaces it with another string (tractionsId), like so:
content = document.body.innerHTML.replace(_aff.tractionsCode, tractionsId);
document.body.innerHTML = content;
Here's an example:
Assume _aff.tractionsCode is equal to {tractionsCode}
<div class="affiliate" rel="{tractionsCode}">{tractionsCode}</div>
It would need to replace both instances of {tractionsCode}
However, the problem is once this happens, it is replacing the loaded html so other javascript event handlers, which I may or (most likely) may not have access to, no longer function properly.
Is there a way to look through the html (including attributes like rel) and run the replace without overwriting all the html?
Or is there a better way entirely?
Thanks for the help!
Ouch! What you're trying to do is probably possible with lots of work and iterating over nodes (not elements)... but why bother? Such work is made easiest by a combination of templates scoped to a subsection of the DOM and event handlers that make use of delegation.
JSRender (https://github.com/BorisMoore/jsrender) is the replacement for the previous jQuery template plugin (https://github.com/jquery/jquery-tmpl), but jquery-tmpl is still rather usable (I believe pandora still uses it). You can store templates in script blocks using a non-javascript type and easily pull that content to render and replace a specific subsection of the DOM:
<script id="affiliateThing" type="text/x-jquery-tmpl">
<div class="affiliate" rel="${transactionsCode}">${transactionsCode}</div>
</script>
<script>
jQuery("#affiliateThing").
tmpl([{'transactionsCode': 'wee'}]).
appendTo("#someElement");
</script>
To ensure that replacing this markup doesn't break any event handlers, make sure to delegate events via a parent element:
jQuery("#someElement").on("click", ".affiliate", function(event){alert('wee');});
This statement binds the event handler to "#someElement" and not the individual ".affiliate" elements, but only fires the handler if the event originated from a ".affiliate" element.
Enjoy!
EDIT: The backend library for this and templating system is completely unimportant. The technique, however, is. Mix scoped templating and delegated events and you're golden.
Going via innerHTML of the body is going to overwrite everything. You need to search for the specific text nodes that you want to replace.
Something like..
document.getElementBy..('identifier').innerHTML
In jQuery,
$('.className').text('text to insert');

html object to javascript

Hello I am having a problem with .html function in jquery. event listener doesn't work anymore everytime i remove the script from the codes and paste it again. can you help me how to reactive script after it's re-paste in html.
You can set your events using .live() method. Like:
$("#submit_button").live("click",function(e){
});
This way if you are adding/removing html from your page using .html() method, the events will remain intact.
Hope that helps.
You could use the .live() method to register the event handler which will preserve it even if the corresponding DOM element is recreated. Example:
$(function() {
$('#someid').live('click', function() {
//
});
});
I guess you are changing the inner html of some container with .html() function of jquery and the events you assigned are lost after the process. There are two approaches you can take:
If the content doesn't change use the .detach() function to remove and insert your elements back. The .detach() function preserves and event handlers attached to the elements you detach. However if you are inserting a different content then use .live() event to assign your events. The events that are assigned with .live() will be recreated when a element with the same selector is inserted into the dom.
Use this as an example:
$('a.button').live('click', function(){
//anchor tag clicked.
alert('Button has been clicked');
});
The .live():
Attach a handler to the event for all
elements which match the current
selector, now and in the future.
The .live() method is able to affect
elements that have not yet been added
to the DOM through the use of event
delegation.
This means that you add and remove html from your page with .html() and your events will still perform.
See the jQuery website for more information on .live()
A quick jsFiddle example.

Using JQuery to get string value of an onclick() event

Wondered if there was good way to do this, thought I would post to the SO community...
There is a 3rd party web page that I have no control over how it renders, but they allow me to add JQuery.
Using the JQuery, I am creating a nav menu on the side of the page, it will be a list of links. The onclick event of these links I get from existing onclick events already on the page, but when I do a:
var linkLoc = $('#theLink').attr("onclick");
linkLoc returns:
function onclick(event) {
handleJumpTo("com.webridge.entity.Entity[OID[E471CB74A9857542804C7AC56B1F41FB]]", "smartform");
}
instead of what I would expect:
handleJumpTo("com.webridge.entity.Entity[OID[E471CB74A9857542804C7AC56B1F41FB]]", smartform");
I think JQuery is trying to get the event for binding, but I need the actual Javascript markup since I'm creating the HTML dynamically. I guess I could substring the "function onclick(event) {" out, but seems kind of hacky.
Any ideas of an elegant way I could get the onclick markup?
$("#theLink") would return a jQuery object whereas $("#theLink")[0] would give a DOM object. This is a resson that $("#thelink")[0].getAttributeNode('onclick').value would work.
The type of $('#theLink').attr("onclick") is a function, so you can just use that when you bind events to the links.
var linkLoc = $('#theLink').attr("onclick");
$('a#link1').live('click', linkLoc);
Example: http://jsfiddle.net/BdU6f/
You can also run other code in the click handler too, if you need:
var linkLoc = $('#theLink').attr("onclick");
$('a#link1').live('click', function(e){
// Code...
linkLoc(e);
});
Example: http://jsfiddle.net/BdU6f/1/
The "onfoo" attributes have values that are functions, not strings. The semantics of:
<whatever onclick='code code code'>
are that the browser constructs a function object as if you had code that did this:
document.getElementById('whatever').onclick = new Function("event", "code code code");
Thus you don't really need the raw string, since you've got something better: the function itself, ready to be called. You can then bind it as a handler to other elements via JavaScript code, not HTML (which is really a better way to do things anyway). You're using jQuery, you say, so you can use the jQuery ".bind()" API to bind those functions to whatever elements you need.
You should also be aware that there are other ways of binding event handlers to elements, ways that will leave the "onfoo" attributes completely unset.
If I understand where you're going with this, you should be able to assign the returned onclick function straight through to the onclick of your new nav element...
$('#NewNavElement').click($('#theLink').attr('onclick'));
If you need to add additional code to the handler, you can just bind another click handler.
try this;
$('#theLink').getAttributeNode('onclick').value
Revised as per comment:
$('#theLink').get().getAttributeNode('onclick').value

How to work with dynamically created fields?

I have web layout, which can contains several links on it. Those links are dynamically created, using AJAX functions. And it works ok.
But, I don't know how can I work with those "dynamically created links" (ie. how to call some JS or jQuery function if I click on them). I guess that browser can not recognize them, since there are created after page is loaded.
Is there some function, that can "re-render" my page and elements on it?
Tnx in adv on your help!
You can use the 2 following methods jQuery provides:
The first one, is the .live() method, and the other is the .delegate() method.
The usage of the first one is very simple:
$(document).ready(function() {
$("#dynamicElement").live("click", function() {
//do something
});
}
As you can see, the first argument is the event you want to bind, and the second is a function which handles the event. The way this works is not exactly like a "re-rendering". The common way to do this ( $("#dynamicElement").click(...) or $("#dynamicElement").bind("click", ...) ) works by attaching the event handler of a determinate event to the DOM Element when the DOM has properly loaded ($(document).ready(...) ). Now, obviously, this won't work with dynamically generated elements, because they're not present when the DOM first loads.
The way .live() works is, instead of attaching the vent handler to the DOM Element itself, it attaches it with the document element, taking advantage of the bubbling-up property of JS & DOM (When you click the dynamically generated element and no event handler is attached, it keeps looking to the top until it finds one).
Sounds pretty neat, right? But there's a little technical issue with this method, as I said, it attaches the event handler to the top of the DOM, so when you click the element, your browser has to transverse all over the DOM tree, until it finds the proper event handler. Process which is very inefficient, by the way. And here's where appears the .delegate() method.
Let's assume the following HTML estructure:
<html>
<head>
...
</head>
<body>
<div id="links-container">
<!-- Here's where the dynamically generated content will be -->
</div>
</body>
</html>
So, with the .delegate() method, instead of binding the event handler to the top of the DOM, you just could attach it to a parent DOM Element. A DOM Element you're sure it's going to be somewhere up of the dynamically generated content in the DOM Tree. The closer to them, the better this will work. So, this should do the magic:
$(document).ready(function() {
$("#links-container").delegate("#dynamicElement", "click", function() {
//do something
});
}
This was kind of a long answer, but I like to explain the theory behind it haha.
EDIT: You should correct your markup, it's invalid because: 1) The anchors does not allow the use of a value attribute, and 2) You can't have 2 or more tags with the same ID. Try this:
<a class="removeLineItem" id="delete-1">Delete</a>
<a class="removeLineItem" id="delete-2">Delete</a>
<a class="removeLineItem" id="delete-3">Delete</a>
And to determine which one of the anchors was clicked
$(document).ready(function() {
$("#links-container").delegate(".removeLineItem", "click", function() {
var anchorClicked = $(this).attr("id"),
valueClicked = anchorClicked.split("-")[1];
});
}
With that code, you will have stored in the anchorClicked variable the id of the link clicked, and in the valueClicked the number associated to the anchor.
In your page initialization code, you can set up handlers like this:
$(function() {
$('#myForm input.needsHandler').live('click', function(ev) {
// .. handle the click event
});
});
You just need to be able to identify the input elements by class or something.
How are these links dynamically created? You can use use the correct selector, given that they are using the same class name or resides in the same tag, etc.
consider the html form
<form>
<input type="text" id="id" name="id"/>
<input type="button" id="check" name="check value="check"/>
</form>
jquery script
$('#check).click(function() {
if($('#id).val() == '') {
alert('load the data!!!!);
}
});
here on clicking the button the script check the value of the textbox id to be null. if its null it will return an alert message....
i thin this is the solution you are looking for.....
have a nice day..
Noramlly , the browser process response HTML and add it to DOM tree , but sometimes , current defined events just not work , simply reinitialize the event when u call the ajax request ..
All you need to do to work with dynamically created elements is create identifiers you can locate them with. Try the following code in console of Firebug or the developer tools for Chrome or IE.
$(".everyonelovesstackoverflow").html('<a id="l1" href="http://www.google.com">google</a> <a id="l2" href="http://www.yahoo.com">yahoo</a>');
$("#l1").click(function(){alert("google");});
$("#l2").click(function(){alert("yahoo");});
You should now have two links where the ad normally is that were dynamically created, and than had an onclick handler added to bring up an alert (I didn't block default behaviour, so it will cause you to leave the page.)
jQuery's .live will allow you to automatically add handlers to newly created element.
If your links are coming in via AJAX, you can set the onclick attributes on the server. Just output the links into the AJAX like this:
Holy crap I'm a link
The return false makes sure the link doesn't reload the page.
Hope this helps!

Categories