In CKEditor 4 I want to fire some action on key and paste events. I've got working code for single event:
$('#some_id').ckeditor({
some: config
}).ckeditor().editor.on('key', function(evt) {
//some action here
});
And I don't want to repeat all code for next event. I've searched ckeditor docs - and it says that on method takes only string, so give it an array of events isn't possible. I've tried pass multiple events as string key, paste - it wasn't best idea. Another way could be made an array of events and iterate it with code above - this solution seems to be not ideal, but the best I can figure out for now. Have You any better ideas for this problem?
Since nobody have any idea in this matter, I finished with best solution I could figure out on this moment: provide array of events and iterate it. I paste here my solution for others facing same dillema:
var editor = $('#textarea').ckeditor({
//some:config
}).ckeditor().editor;
var events = ['event1', 'event2'];
for (event of events) {
editor.on(event, function(evt) {
//Yours actions
}
}
Related
What is the best way to distinguish events in JavaScript.
Actually there are two points I am interested in. The first one is are there something like id in event (it would be very useful foe debugging purposes). And another point are there better ways to distinguish mousedown and mousedown&touchstart events.
Let me tell you my story. I met the problem that if you add two dom events to a node with triggers mousedown and touchstart, then on mobile devices both mousedown and touchstart run.
The first solution I found was to run
event.preventDefault();
event.stopPropagation();
at the beginning of each listener function.
But then I found out an event delegation pattern and started to work with analytics, all that disallowed to use the previous approach, so I come up with the following solution:
let lastEvent = null;
const specificListener = function(e) {
if (lastEvent === e) {
return; //already run the code due to another listener
}
/*
logic goes here
*/
lastEvent = e;
};
And now I am interested whether or not it is possible to compare events in a different way (different from event1 === event2, hope to find out about something like event1.id === event2.id)?
Thank you.
Instead of trying to differentiate the events, just subscribe to mousedown only, as it is fired anyway. That's the most simple solution I'd say.
Or, you could try to detect which event system is supported and only subscribe to the appropriate one:
var eventType = document.ontouchstart != null ? 'touchstart' : 'mousedown';
document.addEventListener(eventType, (e) => { ... });
A third solution (possibly) would be to use PointerEvents only, but that depends on the platforms you need to support.
https://caniuse.com/#search=pointer%20events
If you for sure cannot use one of these approaches: Every event should have a timestamp property (not sure if it is named that way), maybe you can find a way to distinguish two events with it.
My requirements
Because of the asynchronous architecture of my applications I am looking for an 'event' system which has the following two two properties:
The events should be able to fire multiple times (possible with events, but not with promises)
When I start listening for an event that has already been fired, I want the listener to fire once immediately (as with promises)
The reason for 1. is that there are a lot of events (e.g. the updating of certain data) that I want to be able to fire multiple times. But I would like to combine this with 2. so that if an event has already fired upon adding the listener, this listener gets called immediately. This is because I'm not always sure (and I don't want to be sure) which piece of code gets run first.
My 'solution'
I have thought up the following solution. I'm using this in an AngularJS application therefore the AngularJS context, but the question is applicable for Javascript in general. Note that I simplified the code.
app.controller('AppCtrl', function(CustomEventEmitter){
// Broadcast an event. No listener added so nothing happens
CustomEventEmitter.broadcast('event');
// Add the event listener. Because the event allready fired, the listener gets called immediatly
CustomEventEmitter.on('event', function(){
console.log('Event emitted');
});
// Broadcast an other event
CustomEventEmitter.broadcast('event');
});
app.service('CustomEventEmitter', function(){
var
listeners = {},
memory = [];
this.broadcast = function(name){
// The normal broadcasting of the event to the listener
if(listeners[name]) {
listeners[name].forEach(function(listener){
listener();
});
}
// Push the event into the 'memory'
memory.push(name);
};
this.on = function(name, listener){
// The normal adding of the listener
if(!listeners[name]) {
listeners[name] = [];
}
listeners[name].push(listener);
// If an event is already in memory, call the listener
if(memory.indexOf(name) !== -1) {
listener();
}
};
});
My questions
My questions are these:
What is the 'best practice' solution for my requirements?
What do you think of my 'solution'?
Am I missing something completely obvious?
The reason for the last question is that it seems to me that this is a very common design paradigm but I seem unable to find the best way to solve this in simple and concise way.
Note
I understand this can be solved with the adding of extra code (e.g. before adding the listener, check in an other way if the event you are going to listen for already happened) but this is not what I'm looking for.
A "property" from bacon.js does exactly what you are asking for. This falls under the broader category of functional reactive programming (FRP). The most popular two libraries for this in JavaScript are probably
bacon.js
Reactive Extensions
Both of which provide the specific tool you're asking for, along with a vast array of alternatives.
I am trying too keep two instances of an Ace editor in sync. So when the user types in one, the other is updated.
Looking at their docs I see that the EditSession change event says that it returns a delta of the change, and the Document has an applyDeltas method.
So I have hooked into that change event, and when it is fired I call the other document.applyDeltas and pass it over, but it doesn't work.
I have been poking around their docs (and Google for an hour), but I am not seeing how to keep them in sync. Does anyone know how I can do that?
Ok, I figured it out. Nothing beats looking at src :)
The applyDeltas method on the document wants an array, AND you need to grab the data from the change event.
//on editor1.change
this.handleEditor1Changed = function (e) {
var deltas = new Array();
deltas[0] = e.data;
this.editor2.getSession().getDocument().applyDeltas(deltas);
};
If you looked at the source, you should have seen that applyDeltas just calls applyDelta in a loop for each element in the array. Thus, you could simply do this:
editor1.on('change', function(delta) {
editor2.session.doc.applyDelta(delta);
})
or, in more modern JavaScript
editor1.on('change', delta => editor2.session.doc.applyDelta(delta))
I currently am working on a bookmarklet that opens an iframe, and sets up a communication of postMessage back and forth. That all works fine.
However, seemingly because the bookmarklet is being loaded as an anonymous function, the listeners are multiplying if I run the bookmarklet more than once on a page.
Is there some sort of way to keep track of these addEventListeners so that they don't double-up?
Do I need to define the rp_receive_message outside of the anonymous function?
Here's an example of the code:
var rp_receive_message = function (e) {
var response = e.data;
console.log("got message with "+ response);
};
if (window.addEventListener) {
window.addEventListener('message', rp_receive_message, false);
} else {
window.attachEvent('onmessage', rp_receive_message);
}
var s1 = window.document.createElement('iframe');
s1.setAttribute('src', 'http://mydomain.com/iframe.html');
s1.setAttribute('id', 'testiframe');
s1.setAttribute('width', '700');
s1.setAttribute('height', '550');
s1.setAttribute('frameBorder', '0');
s1.setAttribute('onload', 'this.contentWindow.postMessage(window.location.href, "http://mydomain.com/iframe.html");');
document.getElementById('container').appendChild(s1);
Probably this will solve the problem:
window.onmessage = rp_receive_message;
As you suggest, the code below might be enough by itself. I don't know if addEventListener and attachEvent will add the same function multiple times, but I wouldn't at all be surprised if they will. I suggest just testing it.
window.rp_receive_message = function(){...}
If you dislike either solution, you've got to set up a global variable, which hardly seems any different or greatly superior to above. The global can be a simple boolean to check if the event has been attached, or it can be a list of attached events that you update yourself. AFAIK, and I'm pretty sure, there is no native JS solution to get a list of event listeners have been attached to a particular event. Libraries such as jQuery maintain lists and let you read them; and possibly have other techniques that are elegant solutions to your general problem.
I recently found myself in the situation that I needed to remove a function bound to the resize event of the window by WordPress' media manager (media-upload.js), because it was interfering with the proper use of Thickbox. The event is attached like this:
a(window).resize(function(){tb_position()})
It took me a while, but I finally found out I could do it in this way:
jQuery.each( jQuery(window).data('events')['resize'], function(i, event) {
var thisEvent = event.toString().replace(/\n/g, '').replace(/\t/g, '').split(' ').join('');
var expectedEvent = 'function(){tb_position()}';
if (thisEvent == expectedEvent)
delete jQuery(window).data(‘events’)[‘resize’][i];
})
Here I cycle through the events, removing spaces, tabs and new lines from them and compare them to what I'm looking for, and when I find it I throw it out of the goddamn airlock. It happens in this case that the attached function perhaps doesn't have spaces, tabs or new lines, but this way also works with more complicated functions as far as I can tell.
Is there an easier and/or more elegant way of doing this? Is this a recipe for disaster down the road?
When you register a handler for an event, you can use a qualifier:
$('#something').bind('click.removeMeSomeday', function() { ... });
Then when you need to remove it you can do so without bothering other handlers for "click".
Now, it occurs to me that you may not be able to affect the way that Wordpress binds its event handler.
Another way around might be to use WordPress' system for queueing/unqueueing or registering/deregistering scripts. Unregister media-upload.js, and then queue your own version of it.
http://codex.wordpress.org/Function_Reference/wp_enqueue_script
http://phpxref.ftwr.co.uk/wordpress/nav.html?wp-includes/functions.wp-scripts.php.source.html#l74
http://phpxref.ftwr.co.uk/wordpress/nav.html?wp-includes/functions.wp-scripts.php.source.html#l37
Prem