Dojo delegate to DOM Element's ID - javascript

I am working with a JSP that dynamically includes DOM elements such as a buttons based on a user's privilege or other factors.
Now, in my JS, I was using:
dojo.connect(dojo.byId('dispatchBtn'), 'onclick', function() { //logic }
The problem is, if the 'dispatchBtn' doesn't exist because it was stripped based on the user's privilege, the JS just calls all the contents of the "//logic" anyway.
I tried to change this call to:
eventView.delegate(dojo.byId('dispatchBtn'), 'onclick', function() { //logic }
But that doesn't seem to work at all, even for buttons that DO exist. What's the best way to do this without explicitly testing if the button exists. I do not want to change the markup on any of these button elements, no additional classes, just want to reference them by ID.

You can do like jquery live does and bind on the document level.
require(["dojo/on", "dojo/query"], function(on){
on(document, "#dispatchBtn:click", function(evt){
alert("Clicked on node " + this.id);
});
});
Fiddle:http://jsfiddle.net/theinnkeeper/NTn6g/

Why are you adverse to checking if the button exists? That makes the most sense
var btn = dojo.byId("dispatchBtn");
if(btn) {
on(btn, 'onclick', function() {});
}

Related

JQuery select elements in <span> that doesn't exist initially

I've been looking for so long and found several answers that suggest using .on() as in $('.idOfMyElemenet').on() works even for elements that don't exist yet. But this doesn't seem to be finding the element. Am I doing something wrong?
The highest level <span> (in screenshot) does not exist until I click on a drop-down. Ultimately I'm trying to trigger an event when the user clicks on any of the <li> (aka selects an option from the drop-down).
$(document).ready(function () {
var test = "#select2-id_customer-results";
$(test).on("click", function() {
console.log('hello')
})
})
EDIT:
Thanks to Drew Baker - I think his second solution is the way to go. But not quite there yet...
From the select2 documentation
All public events are relayed using the jQuery event system, and they
are triggered on the <select> element that Select2 is attached to.
So I tried listening to it via the id (which doesn't seem to exist but would probably be id_customer) and the class. The class I added below did not work. Is there a way to listen to this using Jquery?
$(document).ready(function () {
// console.log($('#id_customer'));
$('.modelselect2 form-control select2-hidden-accessible').on('select2:select', function (e) {
var data = e.params.data;
console.log(data);
});
});
I'll answer your question, but then give you a better solution.
First, you need to make sure the thing you are attaching .on() to actually exists. I typically use a containing DIV or failing that body or html will work.
Secondly you are missing a parameter that tells jQuery the thing you are looking to watch to be clicked on. In this case, I'm assuming it is the UL tag with the ID you provided.
This should do what you want:
$(document).ready(function () {
$('body').on("click", "#select2-id_customer-results", function() {
console.log('hello')
})
})
But a better solution would be to use the Select2 API to have it tell you when something is selected. This will be way more reliable and should make your code work after upgrades to Select2.
Something like this:
$('select[name="customer"]').on('select2:select', function (e) {
var data = e.params.data;
console.log(data);
});
NOTE: #mySelect2 is probably not what you have. Use whatever ID you used to initialize Select2 in jQuery.
You can read more about that API here: https://select2.org/programmatic-control/events
if your element is dynamically generated and you want to target that specific element. You need to specify a static container/parent element to indicate where it belongs.
Try this:
$( '#dynamicallyAddedElement' ).on( 'click', '#wrapper', function () { ... });
//where #wrapper is a static parent element in which you add the dynamic links.
So, you have a wrapper which is hard-coded into the HTML source code:
PS. Hope I helped in some way.
If you need to trigger an event when click on <li> elements, you have to use that elements id or class as the selector. Check the below code:
$(document).ready(function () {
var test = ".select2-results__option";
$(test).on("click", function() {
console.log('hello')
})
})
It turns out this is an old bug in django-auto-complete.
The code below works. I have no idea why but now I can move on.
Note: the 'name' is the value of the select2 select element (see screenshot at bottom)
document.querySelector('select[name="customer"]').onchange=function() {
console.log("myselect2name changed");
};

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");
}
});
});

How to select parent using on selector

I have a dynamic hover that gets activated based on whether a hidden element exists or not. I'm updating my code to incorporate dynamically created elements but have ran into an issue and don't know how to select a parent.
Previously I used $(".infotip").parent().hover but have updated to:
$(document).on("mouseenter", ".parent-selector", function() {
$(this).find(".infotip").addClass("active");
});
$(document).on("mouseleave", ".parent-selector", function() {
$(this).find(".infotip").removeClass("active");
});
So what needs to happen is I need ".parent-selector" to behave like $(".infotip").parent()
Since the content is dynamic and you mentioned you can't add a class to the parent when it's created, the only way I can think to do this would be to watch for any new elements that have been added and then bind your events.
This function will periodically look for any elements with the .infotip class that does not have our custom events_bound attribute already. If it finds one, it'll add the attribute and then bind the mouse events to the parent. I've included a fiddle illustrating this with dynamic content.
//check for changes in the dom
setInterval(function() {
$('.infotip:not([events_bound])').each(function() {
//add attribute so that we don't re-bind to this element
$(this).attr('events_bound', true);
//now bind the events to the parent
$(this).parent().mouseenter(function() {
$(this).find(".infotip").addClass("active");
})
$(this).parent().mouseleave(function() {
$(this).find(".infotip").removeClass("active");
})
});
}, 500);
https://jsfiddle.net/ybrwv0c8/1/
Of course if there is anything identifiable about the parent, then the best way would be to use a selector for your on. For instance, if there's a dynamically generated ID with a standard structure like parent_13835723, you could do a partial attribute selector like $('[id^=parent_]')
You might also be able to use use the jquery :has pseudoselector like so. However, this searches all descendants for an element, which may not work correctly depending on how your DOM is structured.
$(document).on("mouseenter", ":has('.infotip')", function() {
$(this).children('.infotip').addClass("active");
});
$(document).on("mouseleave", ":has('.infotip')", function() {
$(this).children('.infotip').removeClass("active");
});
However, according to the jquery docs here http://api.jquery.com/has-selector/:
The expression $( "div:has(p)" ) matches a <div> if a <p> exists anywhere
among its descendants, not just as a direct child.
Because :has() is a jQuery extension and not part of the CSS
specification, queries using :has() cannot take advantage of the
performance boost provided by the native DOM querySelectorAll()
method. For better performance in modern browsers, use $(
"your-pure-css-selector" ).has( selector/DOMElement ) instead.
I'm not sure whether the :has or setInterval method would have better performance.
How about
$(".infotip").parent().mouseleave(function() {
$(this).find(".infotip").addClass("active");
}
and
$(".infotip").parent().mouseleave(function() {
$(this).find(".infotip").addClass("active");
}
Reference : https://api.jquery.com/mouseleave/
You can use jQuery's custom :has selector:
$('document').on('mouseenter', ':has(.infotip)', function () {
$(this).find(".infotip").addClass("active");
});
$('document').on('mouseleave', ':has(.infotip)', function () {
$(this).find(".infotip").addClass("active");
});
I haven't tested this, as there is no HTML provided in the question, but the documentation seems to indicate it will do what you want.
As simple as
jQuery(".child").parent().on('mouseenter', function(){
jQuery(this).css('background', '#f00');
});
jQuery(".child").parent().on('mouseleave', function(){
jQuery(this).css('background', '#0ff');
});
DEMO
Edit:- Based on further clarification,
You can attach events to objects when you create them. If you are binding the same events to multiple objects at different times, just create a named function.
OR
A really dirty hack would be to to unbind and rebind the events everytime a hirerchy of elements is added to the DOM.
Something like
var init = function() {
jQuery(".child").parent().off().on('mouseenter', function(){
jQuery(this).css('background', '#f00');
});
jQuery(".child").parent().off().on('mouseleave', function(){
jQuery(this).css('background', '#0ff');
});
};
Just call the method init everytime you add something to the DOM.

Is it possible to capture keydown globally on dynamically added text inputs?

I'm using the following global jQuery to show and hide a loading div for $.ajax calls:
$(document).ajaxStart(function(){
$("#loading").show();
}
$(document).ajaxStop(function(){
$("#loading").hide();
}
This works fine, but I do not want to show the loading div for autocompletes, so I added this:
$("input[type=text]").keydown(function(){
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});
Then, to reset suppressGlobal for "normal" $.ajax calls, this:
var origAjax = $.ajax;
$.ajax = function() {
if (window.suppressGlobal) {
arguments[0].global = false;
}
origAjax.apply(this, arguments);
window.suppressGlobal = false;
};
This all works nicely for text inputs that are constructed with page load. However, I have several situations where text inputs are inserted dynamically on the client-side using jQuery/Javascript, in which case the keydown event does not get bound to the global function. I also tried on:
$("input[type=text]").on('keydown', function(){
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});
But that doesn't work either. Is there a way that I can globally capture the keydown event regardless of when the text input was added? Could I somehow globally capture the addition of text inputs to the DOM and attach the event handler then?
you will have to use $(document).on() for dynamically created controls.
jsfiddle: http://jsfiddle.net/G9qJE/
also you can use: $('body').on
explanation:
When an event is assigned, it's only assigned to elements that currently exist on the page. If you later on other elements, there is nothing watching that watches for those elements too allow them to be used as well.
That is why you need something sitting at the document level which is aware of the event and the elements you want to apply it to, so that it can watch for any new elements that match and apply that event to them as well.
$(document).on("keydown", "input[type=text]", function() {
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});

Observing events on dynamically inserted objects in the DOM

On load, I add a desired behavior on all textareas on a page.
Event.observe(window, 'load', function() {
$$('textarea').each(function(x) {
x.observe('keydown', dosomethinghere)
});
});
This works because the textareas are already in the DOM, but how should I treat textareas that are dynamically added after the page loads (ex: if I have a button that says "Add More"). I would like these newly created textareas to have the same behavior.
The way I do it is by just observing the new textarea when I add it, like this:
function doSomethingWithTextAreas(){
//do something.
}
document.observe("dom:loaded", function() {
$$('textarea').each(function(s){
s.observe('keydown', doSomethingWithTextareas);
});
$('add_more').observe('click', function(){
textarea = new Element('textarea');
textarea.observe('keydown', doSomethingWithTextareas); //Observes the new textarea.
Element.insert($('textarea_container'), {bottom:textarea});
});
});
Consider using jQuery Live.
$.live() would work as STAii mentions, but there is discussion of implementing a similar function in prototype as well. That would probably be of more benefit so you don't have to add another library.
Well, the answer is a bit tricky. The only way to do this is to maintain a cache of events listeners for your textareas. When adding a new textarea to your page, you would need to call Event.stopObserving on all your cached events. You would then call your $$('textarea').each(...) code again to bind to all the elements.
Thankfully, someone has done this for you already in a very handy lightweight prototype extension called lowpro: http://www.danwebb.net/2006/9/3/low-pro-unobtrusive-scripting-for-prototype
You can do what you wish as simply as:
Event.addBehavior({
'textarea:keydown': function(e) {
dosomethinghere(); // e.g. this.hide();
}
});
Then whenever you add a new textarea dynamically, you simply call Event.addBehavior.reload();
I should point out that "e" is the Event object, and "this" is the element inside the context of the function(e) {} definition.
A nice way of doing this is to have the javascript function which adds the text areas fire an event which any other function can observe and act on. So:
function add_textarea() {
// Code creates a new <textarea> and adds it to the page
var textarea = new Element("textarea");
$("some-form").insert(textarea);
textarea.fire("textarea:add")
}
document.observe("textarea:add", function(event) {
event.target.observe('keydown', dosomethinghere);
});
This allows your 2 functions--one that adds a new textarea and one which attaches observers--to be loosely coupled and not know anything about each other. One simply needs to fire a custom event which the other can observe.

Categories