I am creating a CKEditor plugin and I face some issues on this.
Model of My Plugin:
CKEDITOR.plugins.add("myplugin", {
//for lang,require,icon
init:function(a){
editor.on('contentDom', function () {
editor.document.on('key', function (evt) {
console.log("Key Pressed");
});
});
}
});
This is Working Fine.But,my problem is setData.
I am setting data when the user is clicking a file.
After setData the key event is not Working.
Is there any way to attach the listener to document after every setData() within plugin file?
And what are the other type of methods which are used in CKEditor like init ?
(OR)
Is there any way to setData() without affecting contentdom,key events?
Please add the listener to the editor and not to the document. That way it wil not get removed when document is removed:
editor.on( 'instanceReady', function( e ) {
editor.on( 'key', function( e ) {
console.log('test');
});
});
Please see: https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html#event-key
Finally, I found the Answer.
Refer the below Question
CKEDITOR.setData prevents attaching of events with .on function
And goes to the Document in CKEditor Docs Page.
#contentDomUnload
Finally My Code Like this,
editor.on('contentDom', function () {
var editable = editor.editable();
editable.attachListener(editable, 'keyup', function (evt) {
console.log('for key events');
});
editable.attachListener(editable, 'mousedown', function (evt) {
console.log('for click events');
});
});
And It worked very well.
Related
I'm using introjs to build a tour of my application. I've searched in quite a few places online and through the documentation but can't seem to find anywhere a method of how to run a function upon skipping or clicking done on the tour. I'm trying to make it so a cookie is stored and the tour isn't run again until a user requests it or a new user comes to the site. Any help would be great, thanks!
$(function(){
var introguide = introJs();
introguide.setOptions({
showProgress: true,
steps: [
{ hidden }
]
});
introguide.start();
});
This code allows to store the tour info
var introguide = introJs();
window.addEventListener('load', function () {
var doneTour = localStorage.getItem('MyTour') === 'Completed';
if (doneTour) {
return;
}
else {
introguide.start()
introguide.oncomplete(function () {
localStorage.setItem('MyTour', 'Completed');
});
introguide.onexit(function () {
localStorage.setItem('MyTour', 'Completed');
});
}
});
Yes, there is a way but with some caveats.
First, after intro.js is loaded you will have a global called introJs with a property fn (standard jquery plug-in approach).
By setting a function using the oncomplete() function under introJS.fn, you can perform some actions when the user hits the 'Done' button.
Here's an example that just displays a console message:
introJs.fn.oncomplete(function() { console.log("Finished"); });
This works as expected. You can put this in a script anytime after the intro.js library is included.
The 'skip' button functionality, however, will only call the 'oncomplete' handler if you are on the last step. The author of the code views that as not complete and so doesn't run that code as you can see by this extract from the code:
skipTooltipButton.onclick = function() {
if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
self._introCompleteCallback.call(self);
}
_exitIntro.call(self, self._targetElement);
};
This basically says it must be at the last step for this to consider calling the complete callback.
Of course, you could fork the code and remove the restriction. I would suggest if you are going to do that, create a _introSkipCallback in a fashion similar to _introlCompleteCallback and invoke that unless on last step where you might invoke both functions if present.
Hope this helps.
Use oncomplete for functions after 'Done' is clicked
Use onexit for functions after 'Skip' is clicked
Bonus function: onchange will log each step, this can be used to call functions on a particular step
document.getElementById('startButton').onclick = function() {
// log each step
introJs().onchange(function(targetElement) {
console.log(this._currentStep)
if (this._currentStep === 3){
stepThreeFunc()
}
}).start()
// clicking 'Done'
.oncomplete(function(){
someFunc()
})
// clicking 'Skip'
.onexit(function(){
someOtherFunc()
});
};
I've noticed that onexit will be called when you click the done button (which is skip until the last step). onexit does not appear to bind this to the introjs object, so I was able to solve the issue of having onexit called when the walkthrough was completed like this:
// during setup
introJs.oncomplete(handleOnComplete);
introJs.onexit(() => handleOnExit(introJs));
function handleOnComplete() {
console.log(this._currentStep); // this is bound to the introJs object
}
function handleOnExit(introJs) {
const currentStep = introJs._currentStep;
if (currentStep < introJs._options.steps.length) {
doSomethingOnSkip();
}
};
I was going to add a comment, but my rep is too low. I didn't want to answer because I haven't actually tested this, but in version 2.5.0 (maybe previous versions too), there is the onexit function, which I believe is supposed to handle interrupts as well as clicking done at the end. Did you try that?
if ($(".introjs-skipbutton").is(":visible")) {
$( document ).on('click', '.introjs-skipbutton', function(event) {
event.stopPropagation();
event.stopImmediatePropagation();
self.exitTourguide();
});
}
I am using introJS tool in my application to give tour guide information of my application.
I used some functions for handling it dynamically. Here stepsData sending in an array format.
var intro = introJs();
intro.setOptions( {
'nextLabel': 'Next >',
'prevLabel': '< Back',
'tooltipPosition': 'right',
steps: this.stepsData,
showBullets: false,
showButtons: true,
exitOnOverlayClick: false,
keyboardNavigation: true,
} );
hope it will help for handling skip button action.
var self = this; intro.start().onbeforechange( function() { /* skip action*/
if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) {
$( document ).on( 'click', '.introjs-skipbutton', function( event ) {
self.exitTourguide();
});
}
});
skip and done action handling.
/Done click action/
intro.oncomplete( function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });
/* clicking 'Skip' action */ intro.onexit(function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });
Skip to bottom for question
JQuery plugin:
$.fn.myPlugin = function( options ) {
var options = $.extend({
myOption: true,
edit: function() {},
done: function() {}
}, options);
options.edit.call(this);
options.done.call(this);
//plugin guts removed to prevent over complication
return {
edit: function(obj) {
$(obj).closest('#myParent').find('#myInput').autosizeInput(); //plugin to autosize an input
},
done: function(obj) {
$(this).closest('tr').find('td').not('.not').each(function(i) {
//do some things
});
}
}
});
Bear in mind this is a cut down version of my plugin.
Called from page:
$(document).ready(function() {
var myPlugin = $('.editable').myPlugin({
edit: $(this).on('click', '.edit-td', function(e) {
e.preventDefault();
//do some page specific stuff
myPlugin.edit( $(this) ); //call the edit returned function
}),
done: $(this).on('click', '.done-td', function(e) {
e.preventDefault();
//do some page specific stuff
myPlugin.done( $(this) ); //call the done returned function
});
});
});
This works great for the most part, however, what i really want is have functions called from inside my plugin every time a specific callback is triggered - without the need to call from outside the plugin.
I have tried including delegated events in my plugin:
$(this).on('click', '.edit-td', function(e) {
e.preventDefault();
$(this).closest('#myParent').find('#myInput').autosizeInput();
});
$(this).on('click', '.done-td', function(e) {
e.preventDefault();
$(this).closest('tr').find('td').not('.not').each(function(i) {
//do some things
});
});
But when the .edit-td is triggered it propagates and triggers the .done-td event, if i put e.stopPropagation() in the edit-td function (because it has been delegated) edit-td stops firing completely.
And non-delegated method:
$(this).find('.done-td').click(function(e, this) {});
But I can't parse the returned object (this) to the internal function before the internal function has completed. (just comes up undefined or missing formal parameter).
*Skip to here
To avoid the question becoming to localised -
I need to have functions called from inside my
plugin every time a specific callback is triggered.
Without calling it using closures
Something like:
if( $.fn.myPlugin.callback().is('edit') ) {
//fire function
}
I needed to return a function(s) like so:
return {
enable: function(arg) {
//do something
},
disable: function(arg) {
//do something
}
}
That way I can call it from inside my plugin by referencing itself like this:
this.myPlugin().disable();
I'm currently writing a js script that changes some styles around in order to hide a fixed footer and position a header absolutely when using a touch device.
I've been able to successfully implement it with normal text input fields and textareas, but since CKEditor doesn't parse in the DOM as a textarea, I'm forced to use it's focusManager class in order to trigger the change when the user focuses on an instance of it in my site.
The problem is that I've never used CKEditor's API before and I'm having some problems using it's focusManager class after doing some research.
Below is my current script.
It works fine for textareas and text inputs, but not on CKEditor.
From what I understand, where you see "cke_1", that is the instance name of the editor, but it's not working.
Also, I have multiple instances of CKEditor throughout my site and it needs to work on all of them.
Any help would be greatly appreciated.
Thanks!
var focusManager = new CKEDITOR.focusManager(cke_1);
var editor = CKEDITOR.instances.cke_1;
$(document)
.on("focus", "input", function(e) {
$body.addClass('fix');
$('footer').hide();
})
.on("blur", "input", function(e) {
$body.removeClass('fix');
$('footer').show();
});
$(document).on("focus", "textarea", function(e){
$body.addClass('fix');
$('footer').hide();
})
.on("blur", "textarea", function(e){
$body.removeClass('fix');
$('footer').show();
});
$(document).on("focus", editor.focusManager, function(e){
$body.addClass('fix');
$('footer').hide();
})
.on("blur", editor.focusManager, function(e){
$body.removeClass('fix');
$('footer').show();
});
I got this working. Since I have multiple instances of ckeditor, I wrote a function that is called when an instance is created and the user is on a mobile device. This is how I did it:
function renderMobile(){
console.log("Mobile device detected");
// Set focus and blur listeners for all editors to be created.
CKEDITOR.on( 'instanceReady', function() {
var editor;
for(var i in CKEDITOR.instances) {
editor = CKEDITOR.instances[i];
}
var $body = CKEDITOR.document.getBody();
editor.on('focus', function() {
$body.addClass( 'fix' );
});
editor.on('blur', function() {
$body.removeClass( 'fix' );
});
});
}
You don't need to use focusManager class at all. Simply listen on editor#focus and editor#blur (JSFiddle):
// Set focus and blur listeners for all editors to be created.
CKEDITOR.on( 'instanceReady', function( evt ) {
var editor = evt.editor,
body = CKEDITOR.document.getBody();
editor.on( 'focus', function() {
// Use jQuery if you want.
body.addClass( 'fix' );
} );
editor.on( 'blur', function() {
// Use jQuery if you want.
body.removeClass( 'fix' );
} );
} );
CKEDITOR.replace( 'editor', {
plugins: 'wysiwygarea,sourcearea,basicstyles,toolbar',
on: {
// Focus and blur listeners can be set per-instance,
// if needed.
// focus: function() {},
// blur: function() {}
}
} );
I have a ckeditor plugin like this:
CKEDITOR.plugins.add('testplugin', {
init: function (editor) {
editor.on('contentDom', function (e) {
var body = editor.document.getBody();
body.on('mouseup', function (e) {
alert('run!!!!');
});
});
});
});
It work perfect in CKEDITOR version 3 (iframe base)
But when i upgrade to CKEDITOR version 4 (lastest - contenteditable base),
all event fire multiple times when i destroy then re-init ckeditor.
(using CKEDITOR.instants.testEditor.destroy() and CKEDITOR.replace('testEditor',options);)
i use: removeAllListeners( ) to remove all event listeners to body but no change.
How can i complete destroy CKEDITOR 4 + All event listeners on it?
It seems that you use an inline instance, so editor.document is CKEDITOR.document. It means that every single instance shares the same var body = editor.document.getBody();.
To avoid duplicated events as a leftover of attached by "dead editors", you should either listen on editor#destroy and call event#removeListener on every single one, or use editable#attachListener which automates that job for you (jsFiddle):
editor.on( 'contentDom', function() {
var body = editor.document.getBody();
// This listener will be deactivated once editor dies.
editor.editable().attachListener( body, 'mouseup', function() {
console.log( 'run!!!!' );
} );
} );
in some function I remove element like this $('#'+id+' img.load').remove(); , how can i track this event and run custom code?
(function($){
var remove_orig = $.fn.remove;
$.fn.remove = function(){
console.log('Remove called');
remove_orig.apply(this, arguments);
};
})(jQuery);
You can "hook" in to any jQuery function and place your own handling code within (including logging method(s)) which will execute before the native jQuery code is executed.
demo (another version with the selector shown)
Catching the removal is easy using the above overload. Simply alter the hook to fire a trigger before (or after) jQuery gets to it:
(function($){
var remove_orig = $.fn.remove;
$.fn.remove = function(){
this.trigger('removing');
remove_orig.apply(this, arguments);
};
})(jQuery);
$('#foo').bind('removing',function(e){
alert('#foo is being removed');
});
$('#foo').remove();
One way is to "trigger" a custom event (in this example I am using window):
$('#'+id+' img.load').remove();
$(window).trigger("MyElementRemoved", [id]);
Then in another part of your code "handle" the event:
$(window).bind("MyElementRemoved", function(e, elementId) {
alert("element removed: " + elementId);
}