dart MouseEvent toElement null - button

in my program I generate buttons inside an HTML table each with the same onClick.listen listener defined.
In the listener, MouseEvent event.toElement is null.
The table is pre-created in HTML, but the table rows, row cells and buttons in the cells are all generated in dart.
How come event.toElement can be null?
If I try a clean sample app (new web application in dart editor) and create a button, add that to a div, then event.toElement is not null.
So I have a general question: when listening to the onClick event of a button, then receiving the MouseEvent, what can cause the MouseEvent's toElement property to be null?
I am running this in a browser (chrome and firefox) after compiling to JS.
EDIT:
In the HTML file I linked the compiled JS script directly.
Now I changed to link the dart script and the pakcages/browser/dart.js (as in sameple dart web apps). After this change, it works fine (event.toElement != null) in chromium and chrome, but not in firefox. Firefox still has event.toElement == null
What to do??
CODE:
HTML:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="sample_container_id">
<p id="sample_text_id"></p>
</div>
<script type="application/dart" src="button_event.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
DART:
import 'dart:html';
void main() {
ButtonElement b = new ButtonElement();
b.text = 'Press Me';
b.id = 'button_id';
b.onClick.listen(onMarketButtonClick);
querySelector('#sample_container_id').append(b);
}
void onMarketButtonClick(MouseEvent _event) {
querySelector('#sample_text_id').text = _event.toElement.toString();
}
RESULT:
In Chrome, clicking the button outputs 'button'
In FireFox, clicking the button outputs 'null'
USE CASE:
In the call back (onMarketButtonClick), I want to retrieve the button id and custom attributes of the button that was clicked, such that I can identify which button was clicked.
Imagine a calculator app. Buttons of different numbers have different inner-html's and different custom attributes and/or ids. But all have the same onClick listener.
When the listener is called, I need to identify which number was clicked. So I need access to the ButtonElement and its custom attributes and id and inner html etc.
(custom attributes for more complex use cases than the calculator)
Thanks a lot,
Imran

The Firefox property is called relatedEvent. Use that, and you won't get null.
I'm still not sure of the use case, but depending on what information you wanted, you could probably just use currentTarget or target:
querySelector('#sample_text_id').text = _event.currentTarget.toString();
querySelector('#sample_text_id').text = _event.target.toString();

You can basically use EventTarget target property of MouseEvent (link). Here is a basic example:
import "dart:html";
void main() {
document.body.append(new ButtonElement()..text="btn1"..id="btn1");
document.body.append(new ButtonElement()..text="btn2"..id="btn2");
document.onClick.listen((MouseEvent e) => (print(e.target.id)));
}
Whenever, mouse clicks you can check the type of target, which is button for your case. Then, you can do whatever you want with it.

Related

How to Attach Events to Table Checkboxes in Material Design Lite

When you create a MDL table, one of the options is to apply the class 'mdl-data-table--selectable'. When MDL renders the table an extra column is inserted to the left of your specified columns which contains checkboxes which allow you to select specific rows for actions. For my application, I need to be able to process some JavaScript when a person checks or unchecks a box. So far I have been unable to do this.
The problem is that you don't directly specify the checkbox controls, they are inserted when MDL upgrades the entire table. With other MDL components, for instance a button, I can put an onclick event on the button itself as I'm specifying it with an HTML button tag.
Attempts to put the onclick on the various container objects and spans created to render the checkboxes has been unsuccessful. The events I attach don't seem to fire. The closest I've come is attaching events to the TR and then iterating through the checkboxes to assess their state.
Here's the markup generated by MDL for a single checkbox cell:
<td>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select mdl-js-ripple-effect--ignore-events is-upgraded" data-upgraded=",MaterialCheckbox">
<input type="checkbox" class="mdl-checkbox__input">
<span class="mdl-checkbox__focus-helper"></span>
<span class="mdl-checkbox__box-outline">
<span class="mdl-checkbox__tick-outline"></span>
</span>
<span class="mdl-checkbox__ripple-container mdl-js-ripple-effect mdl-ripple--center">
<span class="mdl-ripple"></span>
</span>
</label>
</td>
None of this markup was specified by me, thus I can't simply add an onclick attribute to a tag.
If there an event chain I can hook into? I want to do it the way the coders intended.
It's not the nicest piece of code, but then again, MDL is not the nicest library out there. Actually, it's pretty ugly.
That aside, about my code now: the code will bind on a click event on document root that originated from an element with class mdl-checkbox.
The first problem: the event triggers twice. For that I used a piece of code from Underscore.js / David Walsh that will debounce the function call on click (if the function executes more than once in a 250ms interval, it will only be called once).
The second problem: the click events happens before the MDL updates the is-checked class of the select box, but we can asume the click changed the state of the checkbox since last time, so negating the hasClass on click is a pretty safe bet in determining the checked state in most cases.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
$(document).on("click", ".mdl-checkbox", debounce(function (e) {
var isChecked = !$(this).hasClass("is-checked");
console.log(isChecked);
}, 250, true));
Hope it helps ;)
We currently don't have a way directly to figure this out. We are looking into adding events with V1.1 which can be subscribed to at Issue 1210. Remember, just subscribe to the issue using the button on the right hand column. We don't need a bunch of +1's and other unproductive comments flying around.
One way to hack it is to bind an event to the table itself listening to any "change" events. Then you can go up the chain from the event's target to get the table row and then grab the data you need from there.
You could delegate the change event from the containing form.
For example
var form = document.querySelector('form');
form.addEventListener('change', function(e) {
if (!e.target.tagName === 'input' ||
e.target.getAttribute('type') !== 'checkbox') {
return;
}
console.log("checked?" + e.target.checked);
});

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.

return focus from iframe to parent

I have some keyboard shortcuts binded to the document object using the keydown event (I am using the jquery.hotkeys plugin to do this, but I doubt this matters).
I then have an iframe which I insert dynamically into the DOM and after some actions remove it. My problem is that after removing the iframe, I need to click back on the parent document in order to be able to use the keyboard shortcuts, otherwise the keydown events are not detected.
I have tried using .focus(), .click(), .mousedown() etc on the document element as well as on other elements on the parent page, but I could not get the focus back to the parent page.
How can I get the focus back to the page without requiring the user to click on the page?
Thanks!!
if you have an iframe that is contained in the document you can store the "main" document as a variable prior to creating the iframe.
Then when you remove the iframe just call top.doc.focus() or top.doc.getElement('id').focus().
I just spent a while struggling with a similar issue, and what I've concluded is that the script running in the child frame keeps stealing focus back to that frame. Then when the script terminates, the child frame has already been removed, so the browser doesn't know where to focus anymore.
How I solved this was to create a function in the parent (or top) frame, that first removes the child frame, and then restores focus to itself. Now, from the child frame, I simply call the parent's function, and that fixes it.
Example:
outer.html:
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /></head><body>
Outer content - press [Enter] to load the iframe
<div id="iframeHolder"></div>
<script src="//ajax.googleapis.com/ajax/libs/prototype/1.7.2.0/prototype.js"></script>
<script>
// Create a function for the child frame to call
function regainFocus(){
$('iframeHolder').update('');
window.focus();
}
// When the outer document loads, start handling keystrokes
Event.observe(window, 'load', function(){ Event.observe(window, 'keyup', function(e){
// Catch only the Enter key
if((e.which||window.event.keyCode) != Event.KEY_RETURN) return;
// Construct the iframe, set its src, add it to holder, and focus on it
var frame = $(document.createElement('iframe'));
frame.setAttribute('src', 'inner.html');
frame.observe('load', function(){ this.contentWindow.focus(); });
$('iframeHolder').appendChild(frame);
});});
</script>
</body></html>
inner.html:
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /></head><body>
Inner content - press [Esc] to close the iframe
<script src="//ajax.googleapis.com/ajax/libs/prototype/1.7.2.0/prototype.js"></script>
<script>
// When the inner document loads, start handling keystrokes
Event.observe(window, 'load', function(){ Event.observe(window, 'keyup', function(e){
// Catch only the Esc key
if((e.which||window.event.keyCode) != Event.KEY_ESC) return;
// Call the parent's function
parent.regainFocus();
});});
</script>
</body></html>

Passing arguments from one asp.net page to another using jQuery

I need to pass 4 arguments (3 strings and one comma separated list) from an ASP.NET page to another ASP.NET page using jQuery. The destination page ought to be launched as a separate window, which works fine with the following jQuery snippet:
$('#sourcePageBtn').click(function(){
window.open("destinationPage.aspx");
return false;
});
How can I pass the arguments to the destination page? I am trying to avoid the query string to pass the arguments because:
I don't want to show the url arguments (which can also be very long) in the destination window.
There are some special characters like ',/,\, & etc. in the string arguments.
Please suggest.
Edit:
I'm trying to access the arguments in the script section of the aspx file i.e.
<script language="C#" runat="server">
protected void Page_Load ( object src, EventArgs e)
{
//Creating dynamic asp controls here
}
</script>
My specific need for the arguments in the Page_Load of the script section stems from the fact that I am creating a few dynamic Chart controls in the Page_Load which depend on these arguments.
cheers
Initial Thoughts (before solution created)
Use POST for large data instead of GET. With POST no querystring will be used for data and therefore URL length restriction isn't a concern. (The max URL length differs between browsers so you're right to stay away from it when large data is moving).
Special URL characters can be encoded to be passed in the query string so that shouldn't be an issue.
Alternatively you might store the data on the server side from the first page, and have the second page pick it up from the server side. But this is overkill. And it makes you do unneeded server programming.
Passing state via HTTP calls is standard practice. You shouldn't try to circumvent it. Work with it. All the facilities are built in for you. Now it's just up to jQuery to provide us some help...
Note: Be careful using jQuery for main app features in case JavaScript is disabled in the browser. In most cases your web application should be usable at a basic level even when JavaScript is disabled. After that's working, layer on JavaScript/jQuery to make the experience even better, even awesome.
Edit: Solution (with ASP.NET processing)
Key resources for solution implementation are:
How use POST from jQuery - initiates the request, passes arguments, gets response
jQuery context argument - this is how the popup window DOM is accessed/affected from the main window
How it works: From a main page, a POST occurs and results are displayed in a popup window. It happens in this order:
The main script opens a popup window (if it doesn't already exist)
main script waits for popup window to fully initialize
main script POSTs (using AJAX) arguments to another page (sends a request)
main script receives response and displays it in the popup window.
Effectively we have posted data to a popup window and passed arguments to the processing.
Three pages follow and they constitute the complete solution. I had all 3 sitting on my desktop and it works in Google Chrome stable version 3.0.195.38. Other browsers untested. You'll also need jquery-1.3.2.js sitting in the same folder.
main_page.html
This is the expansion of the logic you provided. Sample uses a link instead of a form button, but it has the same id=sourcePageBtn.
This sample passes two key/value pairs when the POST occurs (just for example). You will pass key/value pairs of your choice in this place.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<a id="sourcePageBtn" href="javascript:void(0);">click to launch popup window</a>
<script>
$(function() {
$('#sourcePageBtn').click( function() {
// Open popup window if not already open, and store its handle into jQuery data.
($(window).data('popup') && !$(window).data('popup').closed)
|| $(window).data('popup', window.open('popup.html','MyPopupWin'));
// Reference the popup window handle.
var wndPop = $(window).data('popup');
// Waits until popup is loaded and ready, then starts using it
(waitAndPost = function() {
// If popup not loaded, Wait for 200 more milliseconds before retrying
if (!wndPop || !wndPop['ready'])
setTimeout(waitAndPost, 200);
else {
// Logic to post (pass args) and display result in popup window...
// POST args name=John, time=2pm to the process.aspx page...
$.post('process.aspx', { name: "John", time: "2pm" }, function(data) {
// and display the response in the popup window <P> element.
$('p',wndPop.document).html(data);
});
}
})(); //First call to the waitAndPost() function.
});
});
</script>
</body>
</html>
popup.html
This is the popup window that is targeted from the main page. You'll see a reference to popup.html in the jQuery script back in the main page.
There's a "trick" here to set window['ready'] = true when the popup window DOM is finally loaded. The main script keeps checking and waiting until this popup is ready.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<!-- The example P element to display HTTP response inside -->
<p>page is loaded</p>
</body>
<script>
$(function() {
window['ready'] = true;
});
</script>
</html>
process.aspx.cs (C# - ASP.NET process.aspx page)
The dynamic server page the arguments are POSTed to by the main page script.
The AJAX arguments arrive in the Page.Request collection.
The output is delivered back as plain text for this example, but you can customize the response for your apps requirements.
public partial class process : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
// Access "name" argument.
string strName = Request["name"] ?? "(no name)";
// Access "time" argument.
string strTime = Request["time"] ?? "(no time)";
Response.ContentType = "text/plain";
Response.Write(string.Format("{0} arrives at {1}", strName, strTime));
}
protected override void Render(System.Web.UI.HtmlTextWriter writer) {
// Just to suppress Page from outputting extraneous HTML tags.
//base.Render(writer); //don't run.
}
}
Results of this are displayed into the popup window by the original/main page.
So the contents of the popup window are overwritten with "[name] arrives at [time]"
Main References: HTTP Made Really Easy, jQuery Ajax members and examples.
If you keep a reference to the new window when you open it, ie var destWin = window.open(...) then you can access the variables and methods on the destWin window object. Alternatively you can "reach back" from the destination window with window.opener.

Resources