How to find some certain event handlers with jQuery? - controls

As many browsers do not show tooltips on disabled form elements (as Opera does), I decided to emulate disabled button with jQuery.
When disabling I just set control's class to disabled and for buttons/submit controls I add an event handler click(function(){return false;})
and I can unbind it later when reenabling the control.
Now the problem is - I need to remove all attached event handlers (click, enter key) from the disabled control, except 'mouseenter' and 'mouseleave' because I am using a custom jQuery based tooltip which needs those events.
And after re enabling the button, I need to restore all handlers back.
I know that I can store the attached event handlers in $.data() but I have no idea, how to gather all the event handlers except 'mouseenter' and 'mouseleave'.
Can you help me?

try this:
<script>
$(function() {
//Lets attach 2 event handlers to the div
$("#el").click(function(){ alert("click"); });
$("#el").mouseover(function(){ alert("mouseover"); });
//We iterate on the div node and find the events attached to it
$.each($("#el").data("events"), function(i, event) {
output(i);
$.each(event, function(j, h) {
output(h.handler);
//DO your unbind here if its not a mouse-enter or a mouseleave
});
});
});
function output(text) {
$("#output").html(function(i, h) {
return h + text + "<br />";
});
}
</script>
<div id="el" style="width: 200px; height: 200px; border: solid 1px red;">Test</div>
<span id="output"></output>

unbind - http://api.jquery.com/unbind/

I wouldn't go to all that work.
Since you have a .disabled class on the disabled elements, I'd just use that as a flag to disabled/enable the functionality by testing for that class in an if() statement, and returning false if the element has the disabled class.
So using the click handler you gave, instead of:
$('someElement').click(function(){return false;});
I'd do this:
$('someElement').click(function() {
if( $(this).hasClass( 'disabled' ) ) {
return false;
} else {
// run your code
}
});
Now when you remove the .disabled class, the input will work again. No unbind/bind or tracing using .data(). Much simpler.

Related

Angular-Kendo window custom actions

I'm trying to create a window with a custom action using Angular-Kendo, and have reached a problem.
When using Kendo (minus angular) i would add functionality like explained here:
window.data("kendoWindow").wrapper.find(".k-i-custom").click(function(e){
alert("Custom action button clicked");
e.preventDefault();
});
However, in Angular-Kendo, access to the window object is by $scope.windowname and is only available after the kendo-window="windowname" directive.
I am currently bypassing this by binding the actions at the k-on-open like...
var firstLoad = true;
this.onOpenCallback = function () {
if (firstLoad) {
$scope.messageBodyWindow.wrapper.find(".k-i-custom").click(function (e) {
alert("OMG");
});
firstLoad = false;
}
This solution, however, feels like a cheap hack. is there a "proper" way to achieve this?
You could wrap the Angular-Kendo directive in a custom directive and put the desired functionality into the link function. This will register your custom binding once without any of this 'first time opening the window boolean' hackery.
<div custom-kendo-window>
</div>
The custom directive contains the kendo directive in its template...
.directive('customKendoWindow', function(){
return {
template: '<div kendo-window="win" k-title="\'Window\'" k-width="600" k-height="200" k-visible="false"> <div id="customAction" style="cursor: pointer;">custom click action</div></div>',
link: function(scope, element, attrs){
$('#customAction').bind('click', function(){
alert('Custom action fired!');
})
}
}
})
Here is a code pen showing the simple wrapper as shown above and then a configurable wrapper with the click binding being set up in the link functions of each of the directives.

CKEDITOR - how to add permanent onclick event?

I am looking for a way to make the CKEDITOR wysiwyg content interactive. This means for example adding some onclick events to the specific elements. I can do something like this:
CKEDITOR.instances['editor1'].document.getById('someid').setAttribute('onclick','alert("blabla")');
After processing this action it works nice. But consequently if I change the mode to source-mode and then return to wysiwyg-mode, the javascript won't run. The onclick action is still visible in the source-mode, but is not rendered inside the textarea element.
However, it is interesting, that this works fine everytime:
CKEDITOR.instances['editor1'].document.getById('id1').setAttribute('style','cursor: pointer;');
And it is also not rendered inside the textarea element! How is it possible? What is the best way to work with onclick and onmouse events of CKEDITOR content elements?
I tried manually write this by the source-mode:
<html>
<head>
<title></title>
</head>
<body>
<p>
This is some <strong id="id1" onclick="alert('blabla');" style="cursor: pointer;">sample text</strong>. You are using CKEditor.</p>
</body>
</html>
And the Javascript (onclick action) does not work. Any ideas?
Thanks a lot!
My final solution:
editor.on('contentDom', function() {
var elements = editor.document.getElementsByTag('span');
for (var i = 0, c = elements.count(); i < c; i++) {
var e = new CKEDITOR.dom.element(elements.$.item(i));
if (hasSemanticAttribute(e)) {
// leve tlacitko mysi - obsluha
e.on('click', function() {
if (this.getAttribute('class') === marked) {
if (editor.document.$.getElementsByClassName(marked_unique).length > 0) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked_unique);
}
} else if (this.getAttribute('class') === marked_unique) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked);
}
});
}
}
});
Filtering (only CKEditor >=4.1)
This attribute is removed because CKEditor does not allow it. This filtering system is called Advanced Content Filter and you can read about it here:
http://ckeditor.com/blog/Upgrading-to-CKEditor-4.1
http://ckeditor.com/blog/CKEditor-4.1-Released
Advanced Content Filter guide
In short - you need to configure editor to allow onclick attributes on some elements. For example:
CKEDITOR.replace( 'editor1', {
extraAllowedContent: 'strong[onclick]'
} );
Read more here: config.extraAllowedContent.
on* attributes inside CKEditor
CKEditor encodes on* attributes when loading content so they are not breaking editing features. For example, onmouseout becomes data-cke-pa-onmouseout inside editor and then, when getting data from editor, this attributes is decoded.
There's no configuration option for this, because it wouldn't make sense.
Note: As you're setting attribute for element inside editor's editable element, you should set it to the protected format:
element.setAttribute( 'data-cke-pa-onclick', 'alert("blabla")' );
Clickable elements in CKEditor
If you want to observe click events in editor, then this is the correct solution:
editor.on( 'contentDom', function() {
var element = editor.document.getById( 'foo' );
editor.editable().attachListener( element, 'click', function( evt ) {
// ...
// Get the event target (needed for events delegation).
evt.data.getTarget();
} );
} );
Check the documentation for editor#contentDom event which is very important in such cases.

How to get a element modification properly integrated in the undo/redo stack in CKEditor 4?

I'm trying to implement my own Image plugin replacement for CKEditor. I bootstraped an implementation from the tutorial at http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1
Right now, the only attribute thing editable is the src
In the code below, $.imagebrowser.openPopup(callback) opens a popup once the user has made his selection, calls callback, with the new src attribute of the image.
This works fine, both for insertion and edition, but there is a glich in the undo / redo integration. A modification of the src attribute made by doubleclicking is not undoable until an othe modification occurs (like typing text). Then the modification of the src attribute seems to be properly integrated in the undo/redo stack, and I can undo and redo it.
Any idea of what I'm doing wrong ?
CKEDITOR.plugins.add( 'customimage', {
// Register the icons. They must match command names.
icons: 'customimage',
// The plugin initialization logic goes inside this method.
init: function( editor) {
editor.on( 'doubleclick', function( event ) {
if(event.data.element.getName() == "img") {
$.imagebrowser.openPopup(function(src) {
event.data.element.setAttribute("src", src);
});
}
});
editor.addCommand( 'insertCustomimage', {
allowedContent: ['img[!src,alt]{width,height,float,margin}'],
// Define the function that will be fired when the command is executed.
exec: function() {
$.imagebrowser.openPopup(function(src) {
editor.insertHtml('<img src="' + src + '" style="width: 400px; height: auto; float: left; margin: 10px 10px;">');
});
}
});
// Create the toolbar button that executes the above command.
editor.ui.addButton( 'Customimage', {
label: 'Image',
command: 'insertCustomimage',
toolbar: 'insert'
});
}
});
I'm not sure this is what your looking for but you can make snapshots:
editor.fire( 'saveSnapshot' );
This will add a state to the Undo/redo stack.
This command should be added before this line:
event.data.element.setAttribute("src", src);
The editor.insertHtml() function has this included in the function. But if you're editing tags you need to do this manually

DOM element reappearing after empty() called

I have a button which when clicked the first time it will load another html page.
When it is clicked the second time it will empty a div of the loaded page.
However, for some reason the loaded content keeps reappearing after the second click....
CSS:
#boatdiv {
width: 100%;
}
.clicked {}
HTML
<button id="load"></button>
<div id="boatdiv"></div>
jQuery
$(document).ready(function() {
$.ajaxSetup ({
cache: false
});
var loadURL = "AjaxLoad_injection.html";
$("#load").on("click", function() {
if(!($(this).hasClass("clicked"))){ //checks if button has NOT been clicked
$("#boatdiv").html("<p>loading...</p>").load(loadURL);
}
else {
$("#boatdiv").empty();
}
$("#boatdiv").toggleClass("clicked");
}
);
}); // end ready
What's going on?
You test $(#load) but toggle $("boatdiv").
Try :
$("#load").on("click", function() {
if(!($(this).hasClass("clicked"))){ //checks if button has NOT been clicked
$("#boatdiv").html("<p>loading...</p>").load(loadURL);
}
else {
$("#boatdiv").empty();
}
$(this).toggleClass("clicked");
});
You are toggling class on wrong element. You want it to toggle on the element being clicked. Same as code I gave you in last post.
Simple to walk through it, you are testing this... so need to toglle the class on this
Use
$(this).togglClass('clicked')
Remember that ajax calls are asynchronous. You may be clicking the button a second time before the ajax call has returned.
You could disable the button during the ajax call, like this:
$('#load').on('click', function() {
if (!$(this).hasClass("clicked")) {
$('#load').attr('disabled', true);
$("#boatdiv").html("<p>loading...</p>").load(loadURL, function() {
$('#load').attr('disabled', false);
});
} else {
$('#boatdiv').empty();
}
//$('#boatdiv').toggleClass("clicked");
$('#load').toggleClass("clicked");
});
The button is disabled before the ajax call. A callback function is passed as a second parameter to the "load()" function. It will be called when the ajax call returns. It will re-enable the button.
EDIT: I missed that the wrong element was getting the class toggled, but I still think you want to disable the button during the ajax call or things can get messed up.

jQuery .on() event doesn't work for dynamically added element

I'm making a project where a whole div with buttons is being inserted dynamically when user click a button, and inside that div there's a button, which when the user click on it, it does something else, like alerting something for example.
The problem is when i press on that button in the dynamically added div, nothing happens. The event doesn't fire at all.
I tried to add that div inside the HTML and try again, the event worked. So i guess it's because the div is dynamically added.
The added div has a class mainTaskWrapper, and the button has a class checkButton.
The event is attached using .on() at the end of script.js file below.
Here's my code :
helper_main_task.js : (that's the object that adds the div, you don't have to read it, as i think it's all about that div being dynamically added, but i'll put it in case you needed to)
var MainUtil = {
tasksArr : [],
catArr : ["all"],
add : function(){
var MTLabel = $("#mainTaskInput").val(), //task label
MTCategory = $("#mainCatInput").val(), //task category
MTPriority = $("#prioritySlider").slider("value"), //task priority
MTContents = $('<div class="wholeTask">\
<div class="mainTaskWrapper clearfix">\
<div class="mainMarker"></div>\
<label class="mainTaskLabel"></label>\
<div class="holder"></div>\
<div class="subTrigger"></div>\
<div class="checkButton"></div>\
<div class="optTrigger"></div>\
<div class="addSubButton"></div>\
<div class="mainOptions">\
<ul>\
<li id="mainInfo">Details</li>\
<li id="mainEdit">Edit</li>\
<li id="mainDelete">Delete</li>\
</ul>\
</div>\
</div>\
</div>');
this.tasksArr.push(MTLabel);
//setting label
MTContents.find(".mainTaskLabel").text(MTLabel);
//setting category
if(MTCategory == ""){
MTCategory = "uncategorized";
}
MTContents.attr('data-cat', MTCategory);
if(this.catArr.indexOf(MTCategory) == -1){
this.catArr.push(MTCategory);
$("#categories ul").append("<li>" + MTCategory +"</li>");
}
$("#mainCatInput").autocomplete("option", "source",this.catArr);
//setting priority marker color
if(MTPriority == 2){
MTContents.find(".mainMarker").css("background-color", "red");
} else if(MTPriority == 1){
MTContents.find(".mainMarker").css("background-color", "black");
} else if(MTPriority == 0){
MTContents.find(".mainMarker").css("background-color", "blue");
}
MTContents.hide();
$("#tasksWrapper").prepend(MTContents);
MTContents.slideDown(100);
$("#tasksWrapper").sortable({
axis: "y",
scroll: "true",
scrollSpeed : 10,
scrollSensitivity: 10,
handle: $(".holder")
});
}
};
script.js : (the file where the .on() function resides at the bottom)
$(function(){
$("#addMain, #mainCatInput").on('click keypress', function(evt){
if(evt.type == "click" || evt.type =="keypress"){
if((evt.type =="click" && evt.target.id == "addMain") ||
(evt.which == 13 && evt.target.id=="mainCatInput")){
MainUtil.add();
}
}
});
//Here's the event i'm talking about :
$("div.mainTaskWrapper").on('click', '.checkButton' , function(){
alert("test text");
});
});
It does not look like div.mainTaskWrapper exist.
From the documentation (yes, it is actually bold):
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page.
[...]
By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
You might want to bind it to #tasksWrapper instead:
$("#tasksWrapper").on('click', '.checkButton' , function(){
alert("test text");
});
You need to specify a selector with on (as a parameter) to make it behave like the old delegate method. If you don't do that, the event will only be linked to the elements that currenly match div.mainTaskWrapper (which do not exists yet). You need to either re-assign the event after you added the new elements, or add the event to an element that already exists, like #tasksWrapper or the document itself.
See 'Direct and delegate events' on this page.
I know this is an old post but might be useful for anyone else who comes across...
You could try:
jQuery('body')on.('DOMNodeInserted', '#yourdynamicallyaddeddiv', function () {
//Your button click event
});
Here's a quick example - https://jsfiddle.net/8b0e2auu/

Resources