How to determine which widget was clicked? Dojo - javascript

Is there a way to observe the mouse click event and determine which widget was clicked?
So basically I wish I could do something like this (on mouse click anywhere on the page)
on("click", function (e) {
//var aWidget = dijit.getEnclosingWidget(e.target);
//var id = aWidget.id
//do something based on the widget id
});

You can but it will requires some extra steps.
Basically, the logic is:
- When you click on a node, you have to go up in the DOM until you find a node with the attribute widgetId
- When you have it, use dijit/registry::byNode to fetch the widget.
If you skip the node traversing, you will find the widget only if the main domNode of the widget was clicked.
require(['dojo/on', 'dijit/registry'], function(on, registry){
on(document, 'click', function(event){
var target = event.target,
widget;
while(!target.getAttribute('widgetId') && target.parentNode) {
target = target.parentNode;
}
widget = registry.byNode(target);
console.warn(widget);
});
});
Be aware, this method will work ONLY if you have 1 dojo instance loaded on in the page.
If you page have more than one dojo instance then the document click event must be attached by every dojo instances.

Might be a lot of overhead but yes you can
require(["dojox/mobile/deviceTheme","dojo/on","dijit/registry"],function(theme,on,reg){
on(document,'click', function(e){
console.log(e.target);
var widget = reg.byNode(e.target);
console.log("foundWidget::" + widget.id);
});
});

Related

Javascript in AEMto add a class if a checkbox is clicked

I am trying to target a class called 'horizontal-video' in a div within an AEM component and if the author has clicked a checkbox that has an ID of 'coral-id-540' I want to add a second class called 'flipped' to the div. Here is the code I wrote that isn't working. Could someone help me figure out why it's not working? The console does not show errors.
var x = document.getElementsByClassName("horizontal-video");
$('#coral-id-540').change(function(){
if($(this).is(":checked")) {
$(this).addClass("flipped");
} else {
$(this).removeClass("flipped");
}
});
It's quite possible you're not waiting for the DOM to completely load, (or at least have this bit of code below the element in question on the page during page load)
Is your code wrapped in $(document).ready(function(){ //your code });?
Also, be aware that any element that is dynamically added to the page by JavaScript/jQuery after page load will not have a listener attached using the method you're using.
To allow dynamically added elements to be included in your listener, you should target an ancestor node and add the listener to that node. In plain English: attach the listener to a "higher up" element. The safest (although slowest) node being document itself, but it's better to target something closer:
$(document).ready(function () {
var $horizontalVideo = $(".horizontal-video"); //You're using jQuery - why not use it here? Also, I always name jQuery objects with a `$` in front as a shorthand to know it's wrapped in a jQuery object. Plus, a more descriptive name will help you immensely.
//replace parent-of-coral with the ID of a parent element that you know exists on DOM ready:
$("#parent-of-coral").on("change", "#coral-id-540", function (e) { //get used to using "e" as the event variable for preventing default / stopping propagation / etc
$this = $(this); //cache $(this) reference rather than creating another jQuery object each time you use it
if ($this.is(":checked")) {
$this.addClass("flipped");
} else {
$this.removeClass("flipped");
}
});
});

Too many listeners for document?

Setup
I've got a reusable custom dropdown menu, the rough framework is something like:
var List = (function() {
// Custom list prototype stuffs...
function ListObj(el, options) {
this._init(el);
}
ListObj.prototype._init = function(el) {
var self = this;
self.menu = el;
}
// Expose an init function
return {
init : function(el, options) {
new ListObj(el, options);
}
};
})();
And each menu is initialized with something like:
var ddlist = document.querySelector('.ddlist');
DDList.init(ddlist, options);
This all works as expected.
What I want
One behaviour I'd like each menu to have is to automatically minimize if a click occurs that isn't within the menu itself.
What I've done
Instead of having a single click listener for the document, and then having to add each menu and check where the click was, I've decided to add the document click listener within the _init method above:
ListObj.prototype._init = function(el) {
var self = this;
self.menu = el;
document.addEventListener('click', function(e) {
var el = e.target;
if(self.menu === el || self.menu.contains(el)) {
// Click was inside menu
// Perform whatever tasks
}else {
// Click was outside of (this) menu, so minimize
self.menu.minimize();
}
});
}
This way, each individual menu is automatically initialized with the behaviour to monitor document clicks, and either minimize if click is not within the menu, or perform whatever task is required.
This works perfectly. Of particular interest to me is that I can dynamically create new dropdown menus without having to add them to the document click listener, and I don't have to fight with event.stopPropagation(); (I'd rather cut off my foot).
But... Will my computer explode?
This is all for a single-page webapp, which means that many dozens or hundreds of these menus could be created (and removed). My concern is that all these document click listeners will pile up and cause performance issues.
If I do something like...
document.getElementById('someMenu').remove();
will JS garbage collection know that it can do away with the one document click listener? Or will the listener persist until the end of days? If the latter, is there any way that I can remove that particular listener when the menu is removed?
An important caviate is that the menu will likely never directly be removed, but rather its parent will be removed - so .remove() will never act on the menu directly.
Much thanks!
Just tested...
I created two menus and then deleted one of them (.remove()). Even though the one menu was removed, the document click listener within still fires for every click. This seems to suggest that garbage collection will not handle this mess, and I will end up with countless listeners.
So, now what?
Give the function a name, so you can then remove the event listener.
ListObj.prototype._init = function(el) {
var self = this;
self.menu = el;
self.click_handler = function(e) {
var el = e.target;
if (!document.contains(self.menu)) { // This element has been removed from DOM
document.removeEventListener("click", self.click_handler);
return;
}
if(self.menu === el || self.menu.contains(el)) {
// Click was inside menu
// Perform whatever tasks
}else {
// Click was outside of (this) menu, so minimize
self.menu.minimize();
}
};
document.addEventListener("click", self.click_handler);
}

Is it possible to make hyperlinks inside nodes in Cytoscape.js?

I want users to be able to click on some text inside the node and do some action, like show some dialog or open some other window. Is this possible?
In the documentation there is something like :
text-events : Whether events should occur on an element if the label receives an event; may be yes or no. You may want a style applied to the text on :active so you know the text is activatable.
it seems like the thing but there is no example how to use it on the label level..
EDIT: example
on the diagram
how I imagine it could be in the code:
label_element.on('tap', function(event){
// cyTarget holds a reference to the label
var evtTarget = event.cyTarget;
//...
});
or
cy.on('tap', function(event){
var evtTarget = event.cyTarget;
//evtTarget holds element but I can somehow get the text which got tap event??
});
According Cytoscape.js documentation, you may try do this using
cy.on('tap')
, like this:
Examples
Bind to events that bubble up from elements matching the specified node selector:
cy.on('tap', 'node', { foo: 'bar' }, function(evt){
console.log( evt.data.foo ); // 'bar'
var node = evt.cyTarget;
console.log( 'tapped ' + node.id() );
});
Bind to all tap events that the core receives:
cy.on('tap', function(event){
// cyTarget holds a reference to the originator
// of the event (core or element)
var evtTarget = event.cyTarget;
if( evtTarget === cy ){
console.log('tap on background');
} else {
console.log('tap on some element');
}
});
At http://js.cytoscape.org/#cy.on
If text-events: yes, tapping a node's label will trigger tap on the node.
If you want to have arbitrary UIs on top of nodes that can be independently interacted with, then you should create a separate layer in the DOM.
Rationale : The labels are relatively simple in Cytoscape.js, because supporting complex ones would be on the same order of complexity as the DOM. In that case, it's better to use the DOM as a layer on top of the graph rather than to reimplement it in Cytoscape.js.

attach event handler for dynamic element

I try to attach event handler for dynamic element but failed.
Here's some of my code. I want event can be called by dynamic element.
Maybe javascript provide function to support attach event for dynamic element automatically without bind it again such as jQuery live or on method.
I want to add functionality to load city data while state or province selected to Magento Sales > Order > Create New Order Page without have to search a correct javascript file and try to bind event on it again.
window.addEventListener('load', function(){
function getCity(){
id= this.selectedIndex;
name= this.name;
getKota(id, name);
}
billing_address_region_id= document.getElementsByName('order[billing_address][region_id]');
shipping_address_region_id= document.getElementsByName('order[shipping_address][region_id]');
var event = new Event('change');
billing_address_region_id[0].addEventListener('change', getCity, false);
shipping_address_region_id[0].addEventListener('change', getCity, false);
billing_address_region_id[0].dispatchEvent(event);
shipping_address_region_id[0].dispatchEvent(event);
});
Thanks a ton
Note: This solution is not tested.
What you could do since the target elements are added dynamically is to register a change event handler to the window, then see whether any of the target element fired the change event if so do your action.
window.addEventListener('change', function (e) {
var el = e.target;
if (el.name == 'order[billing_address][region_id]' || el.name == 'order[shipping_address][region_id]') {
getKota(el.selectedIndex, el.name);
}
});
Your code would work only for already existing elements. I recommend using jQuery.
Using jQuery, you can use "on" method.
$('#elem').on('change', function(){
// Do stuff
});

jquery or js code to obtain exact node data for currently selected form item/text/image in a web page

I want to obtain the exact details for the item on a web page that has been clicked on, using jquery.
That item can be a form item (like a checkbox, text box, text area etc) or section of text (in a paragraph or div or other) or list or image ...
What I figured out is the following--
$(function(){
$('*')
.bind('click', function(event) {
//now obtain details of item that has been clicked on...
});
});
Now, I want the exact details- viz the div id/form id/paragraph #, ie all details for that particular item. How do i get this data? I understand that this data is available in the DOM but I just dont know how to get it in this particular case...
Probably the best way to do to use the target property of the event. By default, this returns a non-jQuery object, which isn't particularly useful, however wrapping it in $() solves this issue:
$(function() {
$(document).bind('click', function(event) {
var element = $(event.target);
alert(element.height()); // Get height
alert(element.attr('id')); // Get ID attribute
// ...
});
});
If you want to fix your current method, inside your click() handler, you can access the properties of that element using .attr(), and friends:
$(function() {
$('*').bind('click', function(event) {
alert($(this).height()); // Get height
alert($(this).attr('id')); // Get ID attribute
// ...
});
});
$(this) in the scope of the function references the element that was clicked. There is a list of functions that will return attributes here and here in the jQuery docs. $.attr('id') will return the element's ID, among other things, and $.data() will return data-* attributes.
To get attributes of parent elements, simply use $(this).parent(). For example, to get the ID of the form that contains the clicked element, use $(this).closest('form').attr('id');. Everything is relative to the clicked element ($(this)), so you can just use the DOM traversal functions.
However, using $('*').bind() is incredibly inefficient; you're binding an event handler to every element on the page, when really you should delegate events with .on() (jQuery 1.7+):
$(function() {
$('body').on('click', '*', function(event) {
alert($(this).height()); // Get height
alert($(this).attr('id')); // Get ID attribute
// ...
});
});
This approach only binds one event to <body> instead of an event to every element on the page.
Use the target of click event on page
$(document).click(function(event){
/* store native dom node*/
var tgt=event.target;
/* store jQuery object of dom node*/
var $tgt=$(tgt);
/* example element details*/
var details={ id : tgt.id, height: $tgt.height(), tag : tgt.tagName}
console.log( details)
})
Look at the event.target, and then you can use jQuery's .parents() method to look at every ancestor:
$(document).on('click', function(event) {
var $t = $(event.target); // the element that was actually clicked
var $p = $t.parents(); // the target's parents
var $form = $p.filter('form').first(); // the enclosing form, if it exists
});

Categories