How to solve duplicate objects in dynamic loading page by using jQuery? - asp.net

I want to solve duplicate objects in dynamic loading content. Please look at the following source code for easier understand.
Base Page HTML With 1 Dynamic Loading Content
<body>
<div id="general-div"></div>>
<div id="div1"></div>
<div id="placeholder1">
Dynamic Content will be placed inside this.
<div class="inner-div"></div>
<div class="div1"></div>
</div>
</body>
For script in header of this page, it's very easy to select "general-div" object like the following code.
$('#general-div')
It's quite easy for select "inner-div" object inside placeholder1. So I can select by using the below code.
$('.inner-div')
The above code could works perfectly. However, I can't use the above code when there is more than 1 duplicated object in the same document like the following HTML. The above code will return 2 objects that don’t what I want.
Base Page HTML - After load another dynamic loading content
<body>
<div id="general-div"></div>>
<div id="div1"></div>
<div id="placeholder1">
Dynamic Content will be placed inside this.
<div class="inner-div"></div>
<div class="div1"></div>
</div>
<div id="placeholder2">
Dynamic Content will be placed inside this.
<div class="inner-div"></div>
<div class="div1"></div>
</div>
</body>
Possible Solution 1
I must create specified jQuery object foreach script in dynamic loading content like the following code.
// Deep copy for jQuery object.
var specfiedjQueryObj = $.extend(true, {}, jQuery);
// modify find function in jQuery object.
specfiedjQueryObj.fn.find = function(selector)
{
// by adding placeholder selector before eval result.
return new specfiedjQueryObj.fn.old_find('#placeholder1 ' + selector);
};
// So, I can select any object in dynamic loading content.
(function($)
{
// This result must be 1 object.
$('.div1');
})(temp);
Even though, this solution should be work great. But I found that jQuery is a very complex object. I found a lot of errors when I try to use it.
Do you have any idea for solving this problem?
PS.PlaceHolder Id is not a fixed Id. So, It's impossible to fixed it in selector rule. Moreover, I do not know exactly amount of element and position (first, last or middle) of it in document. Because of dynamic loading content will be displayed on a lot of page.

How about $('div[id^=placeholder]:last') ?
Selectors / attrubuteStartsWith

You could simply use $('.innerdiv:first') to get the first one or $('.inner-div:last') to get the last one. Or if you have multiples and want to select a particular one $('.inner-div:nth(x)') where x is the index of the item.

The following function will process data from partial loading view page and add specified context for each jQuery selector in script. This answer works well. However, it does not support external script file.
function renderPartialView(control, data)
{
// For detecting all script tag in loaded data.
var reExtractScript = /(<script type="text\/javascript">)([\s\S]+?)(<\/script>)/gi;
// For detecting all "$" sign (must be jQuery object) in loaded data.
var reFindDollarSign = /\$\(([\S]+?)\)/gi;
// Find all matched string in loaded data.
var result = reExtractScript.exec(data);
var allScript = '';
if (result)
{
for (i = 0; i < result.length; i += 4)
{
// Remove current script from loaded script.
data = data.replace(result[i], '');
// Replace all "$" function by adding context parameter that is control.
allScript += result[i+2].replace(reFindDollarSign, '$($1, "' + control + '")');
}
}
// Load non-script html to control.
$(control).html(data);
// Evaluate all script that is found in loaded data.
eval(allScript);
}
// This script will partially download view page from server in the same domain
$(function()
{
$.get(getUrl('~/Profile/Section/ViewEducation'), null, function(data)
{
// When partial loading is complete, all loaded data will be sent to “renderPartialView” function
renderPartialView('#education-view', data);
});
});

Okay, so let's talk about your example HTML. I added a class of placeholder, and added a dash in the id for convenience later.
<div id="placeholder-1" class="placeholder">
Dynamic Content will be placed inside this.
<div class="inner-div">baz</div>
<div class="div1">zip</div>
action
</div>
<div id="placeholder-2" class="placeholder">
Dynamic Content will be placed inside this.
<div class="inner-div">foo</div>
<div class="div1">bar</div>
action
</div>
Now I can bind an event to each of these links with $('.placeholder a.action').bind('click', ... ); If I want this event to all future chunks of html like this on the page, I do $('.placeholder a.action').live('click', ... );
This code will attach an event to those links and the var statements can capture the id, or the inner text of the <div>s. In this way you don't have colliding id attribute values, but you can traverse actions inside divs with class placeholder.
$('.placeholder a.action').live('click', function(){
// get the id
var id = $(this).parents('div.placeholder').attr('id').split('-')[1];
// get the text inside the div with class inner-div
var inner_div = $(this).parents('div.placeholder').children('.inner-div').text();
// get the text inside the div with class div1
var div1 = $(this).parents('div.placeholder').children('.div1').text();
return false;
});

Related

Render variable partial

I'm using Handlebars together with BackboneJS. I have Backbone Views that extend each other, e.g. a ModalView and a SpecificModalView. ModalView has a Handlebar template, something like this (simplified):
<div class="modal-header">
{{modalTitle}}
</div>
<div class="modal-body">
{{modalBody}}
</div>
<div class="modal-footer">
{{modalFooter}}
</div>
Now, the modalBody is usually a bit more complex than just a regular placeholder like the title and is defined by the SpecificModalView. What I want is that the SpecificModalView can override the modalBody with a partial or an HTMLElement object.
Is this beyond the scope of Handlebarjs and should I just use jQuery to find the modal-body and replace it's content with whatever is passed as modalBody? Or can Handlebarjs deal with variable partials and HTMLElements?
One idea I tried was that SpecificModalView registered a helper called modalBody and returned whatever body required for that modal. The return value of helpers seems to be type casted to a string though.
Thanks for any help.
Ok I just found one solution, you can return HTML from a helper with the Handlebars.SafeString object. So the SpecificModalView can do something like this:
hbs.registerHelper('modalBody', function() {
var tpl = require('hbs!templates/SpecificModalBody'),
return new hbs.SafeString(tpl());
});
(I'm using requirejs)
This offers the flexibility of creating an HTML object with jQuery or using another Handlebar template for the content.
I'm open to any other solutions though.

What is the 'angular' way of displaying a tooltip / lightbox?

I've been looking around and have not been quite able to get a clear path to the 'angular' way of accomplishing the following. What I'm trying to achieve is displaying a tooltip with information when hovering over a link within an ng-repeat loop. Based on my research, I understood that this is part of the view, and so I should probably handle this in a directive. So, I created an attribute directive called providertooltip. The html declaration is below:
<table>
<tr id="r1" ng-repeat="doc in providers">
<td>
<a providertooltip href="#{{doc.Id}}" ng-mouseover="mouseOverDoc(doc)" ng-mouseleave="mouseLeave()">{{doc.FirstName}} {{doc.LastName}}</a>
</td>
</tr>
</table
<div id="docViewer" style="display:hidden">
<span>{{currentDoc.FirstName}} {{currentDoc.LastName}}</span>
</div>
In the module, I declare my directive, and declare my mouseOver and mouseLeave functions in the directive scope. I also 'emit' an event since this anchor is a child scope of the controller scope for the page. On the controller function (docTable ) which is passed as a controller to a router, I listen for the event. Partial implementation is seen below:
app.directive("providertooltip", function() {
return {
restrict : 'A',
link: function link(scope, element, attrs) {
//hover handler
scope.mouseOverDoc = function(doc){
scope.currentDoc = doc;
scope.$emit('onCurrentDocChange');
element.attr('title',angular.element('#docViewer').html());
element.tooltipster('show');
//docViewer
};
scope.mouseLeave = function() {
element.tooltipster('hide');
}
}
}});
function docTable(docFactory, $scope, $filter, $routeParams) {
$scope.$on('onCurrentDocChange',function(event){
$scope.currentDoc = event.targetScope.currentDoc;
event.stopPropagation();
});
}
Ok, so here is my question. All of the works as expected; Actually, the tooltip doesn't really work so if someone knows a good tooltip library that easily displays div data, please let me know. But, what I'm really confused about is the binding. I have been able to get the tooltip above to work by setting the title ( default tooltip behavior ), but I can see that the binding has not yet occured the first time I hover of a link. I assume that the onCurrentDocChange is not synchronous, so the binding occurs after the tooltip is displayed. If I hover over another link, I see the previous info because as I mentioned the binding occurs in an asynchronous fashion, i.e., calling scope.$emit('onCurrentDocChange') doesn't mean the the parent scope binds by the time the next line is called which shows the tooltip. I have to imagine that this pattern has to occur often out there. One scope does something which should trigger binding on some other part of the page, not necessarily in the same scope. Can someone validate first that the way I'm sending the data from one scope to the other is a valid? Moreover, how do we wait until something is 'bound' before affecting the view. This would be easier if I let the controller mingle with the view, but that is not correct. So, I need the controller to bind data to the scope, then I need the view to 'display a tooltip' for an element with the data. Comments?
To go the angular way correctly start your directive like:
...
directive('showonhover',function() {
return {
link : function(scope, element, attrs) {
element.parent().bind('mouseenter', function() {
element.show();
});
element.parent().bind('mouseleave', function() {
element.hide();
});
}
...
Or start with http://angular-ui.github.io/ link to go the angular-way UI. Look into the bootstrap-ui module - pure angular bootstrap widgets implemented as directives. You can get a clue how the tooltip binding implemented directly from the source of the module - https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js
Also here is another example - (having jQuery and bootstrap scripts included) - use the ui-utils module Jquery passthrough directive ui-jq'. It allows to bind Jquery plugins ( style of $.fn ) directly as angular directive.
Here is their example for binding twitter bootstrap tooltip.
<a title="Easiest. Binding. Ever!" ui-jq="tooltip">
Hover over me for static Tooltip</a>
<a data-original-title="{{tooltip}}" ui-jq="tooltip">Fill the input for a dynamic Tooltip:</a>
<input type="text" ng-model="tooltip" placeholder="Tooltip Content">
<script>
myModule.value('uiJqConfig', {
// The Tooltip namespace
tooltip: {
// Tooltip options. This object will be used as the defaults
placement: 'right'
}
});
</script>
Also look into the official angular documentation for writing directives examples,
and have a happy coding time with Angular!

select2 AJAX control disturbs tab ordering

I am using select2 on dropdownlist of asp.net. The code is as follows:
<script type="text/javascript" src="js/select2.min.js"></script>
<link type="text/css" rel="Stylesheet" href="css/select2.css" />
var v = /* get the select control */
v.select2();
The problem is, once select2() function is called, tab ordering stops working. Therefore, when on the dropdownlist tab key is pressed, focus do not move to the control having next highest tabindex but move seemingly randomly to some other control.
Commenting the line where the function is called solve this problem but I need the filtering. I have tried some of the other techniques of filtering discussed here but they are too complicated. Select2 is very simple and useful because all you have to do is include the JS and CSS files and call the function.
How can I solve this ordering problem? Alternatively, is there another filtering option as easy to use as select2 that would help me?
After a few hours of struggle, I have solved the problem. It turns out that the select2 AJAX control do destroy the tab order if the tab is pressed as soon as it gets focus, that is, when nothing is typed in it. It does not, however, destroy tab ordering if some text is typed.
The internal structure of select2's auto-generated HTML is like following:
<div class="select2-container">
<a class="select2-choice">
<span</span>
<abbr class="select2-search-choice-close" />
<div> <b></b> </div>
</a>
<div class="select2-drop select2-offscreen">
<div class="select2-search">
<input class="select2-input select2-focused" tabIndex=<somevalue> />
</div>
<ul class="select2-results></ul>
</div>
</div>
If some text is typed in the HTML select control, then tab ordering is working correctly, if no text is typed then tab order is destroyed. I have used document.activeElement in firebug to find focused control in both cases. In case of no text the anchor element has focus, and in case of text typed the HTML input element has focus.
As shown above, while select2.js correctly set tabIndex property of HTML input element, it does not of the anchor element.
Solution
Just add the following line at the position specified further below in select2.js:
this.container.find("a.select2-choice").attr("tabIndex", this.opts.element.attr("tabIndex"));
Add the line after:
this.opts.element.data("select2", this).hide().after(this.container);
this.container.data("select2", this);
this.dropdown = this.container.find(".select2-drop");
this.dropdown.css(evaluate(opts.dropdownCss));
this.dropdown.addClass(evaluate(opts.dropdownCssClass));
this.dropdown.data("select2", this);
this.results = results = this.container.find(resultsSelector);
this.search = search = this.container.find("input.select2-input");
and before:
search.attr("tabIndex", this.opts.element.attr("tabIndex"));
this.resultsPage = 0;
this.context = null;
// initialize the container
this.initContainer();
this.initContainerWidth();
installFilteredMouseMove(this.results);
this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
installDebouncedScroll(80, this.results);
this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
So it becomes:
this.results = results = this.container.find(resultsSelector);
this.search = search = this.container.find("input.select2-input");
this.container.find("a.select2-choice").attr("tabIndex", this.opts.element.attr("tabIndex")); /* atif */
search.attr("tabIndex", this.opts.element.attr("tabIndex"));
this.resultsPage = 0;
this.context = null;
Make this change in select2.js. Obviously, you need to use the full js version, not the min version.
All you have to do is add one line, stated above. This would become line no#504 in VS2008 if done correctly.

ASP.net MVC 2 SPARK - Create "Add New Item" link in spark page

I have a web form in SPARK which allow the editing of a Facility class that contains Rooms. When editing the Facility all the Rooms are listed for editing too. The form works fine for editing, but I would like to include a button "Add Room" that adds a new blank room below the existing ones. Any idea how this is accomplished?
Currently I am doing this in my SPARK page:
[All the Facility editing stuff...]
<p>Room</p>
<div class="small">Enter the rooms associated with this facility.</div>
<div class="add">
<div id="room">
<AddFacilityRoom each="var roomModel in Model.FacilityRooms" RoomModel="roomModel" Index="roomModelIndex" />
</div>
<a id="addRoom" class="add" href="events/room/add.mvc">Add a room</a>
</div>
AddFacilityRoom contains the html elements for editing a room.
I would like add.mvc to create a new empty Room class and inject a new identical (but empty) control below the existing ones. Currently, though it opens a new page when the "Add a Room" button is clicked.
Ok, I figured this out. I was missing the JQuery knowledge to understand this. The function below:
$('#addRoom').click(function () {
var a = $(this);
a.addClass('loading');
$.ajax({
url: a.attr('href'),
cache: false,
success: function (html) {
$('#room').append(html);
a.removeClass('loading');
}
});
return false;
});
Plus, the following HTML:
<div id="room">
<a id="addRoom" class="add" href="events/room/add.mvc">Add a room</a>
</div>
Does the trick.
Your solution looks nice, but since you are using Spark, you could consider the rarely mentioned Javascript templates. The advantage of this being that the markup in _AddFacilityRoom.spark would not need to be duplicated in add.mvc. Nor would a json request be required (if no data needed for new room).
I'll sadly forgotten exactly how they work, but the steps are something like:
Add a new action:
public ActionResult AddRoomScript()
{
return new JavascriptViewResult { ViewName = "_AddFacilityRoom" };
}
Add a script tag with: src="!{Url.Action("AddRoomScript")}"
Then some js to call and set:
var html = Spark.Shared._AddFacilityRoom.RenderView( { RoomModel = {} );
$('#room').append(html);
Some research would be needed to get that working correctly, but it's an interesting option.

Why does document.getElementById('hyperlink_element_id') return the value of the hyperlink's href attribute?

I'm trying to grab the Web.UI.WebControls.HyperLink object itself via javascript so that I can modify its ImageUrl.
Here I'm setting the hyperlink's NavigateUrl to the my javascript function call:
lnkShowHide.NavigateUrl = String.Format(
"javascript:ShowHideElement('{0}');", lnkShowHide.ClientID
)
Here's my javascript function:
function ShowHideElement(img) {
   var ele = document.getElementById(img);
   if(ele != null) {
      // Not sure if this will change the hyperlink's ImageUrl property???
      img.src = 'smallPlus.gif';
   }
}
However, if I check the value of 'ele' after calling getElementById it prints "String.Format("javascript:ShowHideElement....." and doesn't actually get the hyperlink object itself.
Any ideas would be greatly appreciated!
Why does document.getElementById() return the value of the hyperlink's href attribute?
It doesn't. But when you “alert(element)”, alert() calls toString() on the element, and HTMLLinkElement.toString() returns the contents of the href attribute, so “alert(link)” spits out the same results as “alert(link.href)”.
(Which is a bit weird and confusing, but that's how JavaScript 1.0 worked so there's not much can be done about it now.)
I check the value of 'ele' after calling getElementById it prints "String.Format("javascript:ShowHideElement....."
That shouldn't happen with the exact example you've given... there's no way the server-side “String.Format...” code should make its way through to the client side unless you accidentally enclosed it in quotes, eg.:
lnkShowHide.NavigateUrl = "String.Format(...)";
Other problems that spring to mind are that the function changes name (ShowHideElement/ShowHideImage), and you appear to be trying to set ‘.src’ on the link element (<a>). Links don't have .src, only images do.
Anyhow, you probably don't want to do a show/hide widget like this. javascript: URLs are always the wrong thing, and your example involves a lot of nested strings inside each other which is always fragile. You could try an ‘unobtrusive scripting’ approach, generating markup like:
<div class="showhide"> blah blah blah </div>
With JavaScript to add the open/close functionality at the client side (so non-JavaScript UAs and search engines will see the whole page without hiding bits). eg.:
function ShowHider(element) {
var img= document.createElement('img');
element.parentNode.insertBefore(img, element);
function toggle() {
var show= element.style.display=='none';
element.style.display= show? 'block' : 'none';
img.src= '/images/showhide/'+(show? 'open' : 'closed')+'.gif';
img.alt= show? '-' : '+';
img.title= 'Click to '+(show? 'close' : 'open');
}
img.onclick= toggle;
toggle();
}
// Apply ShowHider to all divs with className showhide
//
var divs= document.getElementsByTagName('div');
for (var i= divs.length; i-->0;)
if (divs[i].className=='showhide')
ShowHider(divs[i]);

Resources