In CKEditor 4 application (working jsFiddle ) I do not want the default drop functionality to be executed in case "special" element is dropped. So I created a listener on "drop" event where I call a function editor.execCommand("nonEditableBlock" that I created. This function inserts a block. It also inserts text. I do not want the text to be inserted. I tried to insert "" or null but it causing other issues. So I thought I would quit the drop event. I tried
evt.stop(); // works only 1st time of dropping
evt.data.preventDefault() // not a function
evt.data.preventDefault(true) // not a function
evt.preventDefault() // not a function
evt.cancel() // works only 1st time
return false // works only 1st time
evt.stopPropagation() // not a function
evt.data.stopPropagation() // not a function
Would you know how to prevent the default or how to not to paste the dragged text?
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.on('drop', function (evt) {
console.log('Drop started')
var timestamp = " [["+new Date().toLocaleString().replace(',','')+"]]"
var data = evt.data.dataTransfer.getData('block')
if (data){
var dataTransfer = evt.data.dataTransfer;
// dataTransfer.setData( 'text/html', null ); //set data
dataTransfer.setData( 'text/html', "xxx "+timestamp ); //set data
console.log("nulling data",evt)
console.log("nulling data",evt.data)
}
console.log("testing drop 1")
editor.execCommand("nonEditableBlock", {startupData: {
id: data.data("id"),
content: data.data("data"),
timestamp:timestamp,
}});
console.log("testing drop 2")
// Stop execute next callbacks.
//evt.stop(); // works only 1st time
// Stop the default event action.
// evt.data.preventDefault() // not a function
// evt.data.preventDefault(true) // not a function
// evt.preventDefault() // not a function
// evt.cancel() // works only 1st time
//evt.stopPropagation() // not a function
//evt.data.stopPropagation() // not a function
console.log("testing drop 3")
//return false // works only 1st time
})
What I find confusing is that
if evt.cancel() is used then drop event is fired but editor.execCommand(" command is NOT executed. Although it is inside the event. See the console for debugging. On the first run the command is called so you can see nonEditableBlock widget called in the console.
if you create empty line by pressing enter then it works. In fact in the editor will be only blocks. Nothing except block will be allowed. No text, no empty lines.
Related
I have a button on my web application, which has the following code in the click event handler:
const fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
fileInputEl.addEventListener('input', (e) => {
if (!e.target.files.length) {
return;
}
// Handle files here...
});
fileInputEl.dispatchEvent(new MouseEvent('click'));
Sometimes (about 1 out of 8), after selecting the file, the input event doesn't fire after choosing a file. I'm guessing this is a browser bug around the lifecycle of the element.
Any way around this short of appending the element to the page and removing it later? What's the proper way to handle this in modern browsers these days?
I'm testing with Google Chrome on Windows.
JSFiddle: http://jsfiddle.net/pja1d5om/2/
Citate from your question: Sometimes (about 1 out of 8), after selecting the file, the input event doesn't fire after choosing a file.
I can confirm this behavior with input and with change events using Opera (ver. 55.0.2994.61, newest version at this time) which uses Google Chrome browser engine "Blink". It happens about 1 out of 25.
Solution
This happens because sometimes your input element object was deleted after file dialog closing because it is not in using anymore. And when it happens you have not the target which could receive input or change event.
To solve this just add your input element somewhere to the DOM after creating as hidden object like follows:
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
And then when the event was fired you can delete it like follows:
document.body.removeChild(fileInputEl);
Full example
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
fileInputEl.addEventListener('input', function(e)
{
// Handle files here...
console.log('You have selected ' + fileInputEl.files.length + ' file(s).');
document.body.removeChild(fileInputEl);
});
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
<input type="button" onclick="selectFile()" value="Select file">
Citate from your bounty description: Bounty will be awarded to someone who ... show an appropriate workaround.
My old suggested workaround (now irrelevant)
We can use setInterval function to check if input value was changed. We save intervalID in our new fileInputEl as property. Because we always create a new file input element then its value is always empty on start (on each button click). And if this value was changed we can detect it when we compare it with empty string. And when it happens then we pass our fileInputEl to fileInputChanged() function and clear/stop our interval function.
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.intervalID = setInterval(function()
{
// because we always create a new file input element then
// its value is always empty, but if not then it was changed:
if(fileInputEl.value != '')
fileInputChanged(fileInputEl);
}, 100);
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
function fileInputChanged(obj)
{
// Handle files here...
console.log('You have selected ' + obj.files.length + ' file(s).');
clearInterval(obj.intervalID);
}
<input type="button" onclick="selectFile()" value="Select file">
It seems this is a browser bug/fluke and likely has something to do with garbage collection. I can get around it by adding the file input to the document:
fileInputEl.style.display = 'none';
document.querySelector('body').appendChild(fileInputEl);
When done, it can be cleaned up with:
fileInputEl.remove();
I am stuck on getting a timeout working. I already have a working code but it seems to me the wrong way to do it.
Working code but probably not the best:
/* Autosave */
// On load we hide all autosave messages.
$('.jform_params_autosave-cg').hide();
// Below is the function that handles the autosave.
$.fn.autoSave = function(){
// We remove the autosave message from it's place defined by the xml and add it to the system message container.
var autosavemessage = $('.jform_params_autosave-cg');
autosavemessage.detach();
autosavemessage.appendTo('#system-message-container');
// Now we show the message.
$('.jform_params_autosave-cg').show();
// Here we save the extension.
Joomla.submitbutton('module.apply');
}
// On change of the below elements we run the autosave.
//------------------------------------------//
// DUPLICATE AUTOSAVE FUNCTION BELOW
//------------------------------------------//
// Autosave: Theme Selection
$("#jform_params_theme_selection").change(function() {
$.fn.autoSave();
});
// Autosave: Add Content
$("a.group-add.btn.btn-mini.button.btn-success").click(function() {
setTimeout(
function()
{
$.fn.autoSave();
}, 5000);
});
The Function:
$.fn.autoSave = function(){
// We remove the autosave message from it's place defined by the xml and add it to the system message container.
var autosavemessage = $('.jform_params_autosave-cg');
autosavemessage.detach();
autosavemessage.appendTo('#system-message-container');
// Now we show the message.
$('.jform_params_autosave-cg').show();
// Here we save the extension.
Joomla.submitbutton('module.apply');
}
The Function Call
$("#jform_params_theme_selection").change(function() {
$.fn.autoSave();
});
The Function Call with Timeout
$("a.group-add.btn.btn-mini.button.btn-success").click(function() {
setTimeout(
function()
{
$.fn.autoSave();
}, 5000);
});
What do I want to achieve
Make the Timeout inside the function.
Define the timeout when calling the function.
With defining I mean calling it something like $.fn.autoSave(5000); or $.fn.autoSave().timeout(500);
I have been trying to get a working code but so far no luck. Will keep updating this post whenever I get more success or details to add.
Thanks everyone for helping.
Any link to existing SO questions will also be appreciated as I might be googling for the wrong key words.
Here it is the modified version of your function. Now it has optional timeout parameter. You can use it like
$('selector').autoSave(5000) or $('selector').autoSave()
$.fn.autoSave = function(timeout) {
function doIt() {
// We remove the autosave message from it's place defined by the xml and add it to the system message container.
var autosavemessage = $('.jform_params_autosave-cg');
autosavemessage.detach();
autosavemessage.appendTo('#system-message-container');
// Now we show the message.
$('.jform_params_autosave-cg').show();
// Here we save the extension.
Joomla.submitbutton('module.apply');
return this;
}
timeout = Number(timeout) || 0;
var f = doIt.bind(this);
if(timeout < 0) return f();
setTimeout(f, timeout);
return this;
}
I have following method which execute a video clip and on progress of the video clip it does one alert, after that alert it was suppose to close the listener but it is not ending the event listener as a result the alert keep showing forever like infinite.
Is this a BUG? or there is something missing in my following code?
function video_present(input) {
$('#mediaplayer').prop('loop', false);
$('#mediaplayer').attr('src', filename).show();
mediaplay_video= document.getElementById('mediaplayer');
mediaplay_video.play();
// STOP repeating??
mediaplay_video.addEventListener('timeupdate', function() {
var sss = parseInt(mediaplay_video.currentTime % 60);
show_second();
}, false);
}
// kill Event after 1 time execute of this
function show_second() {
alert('I was executed - stop me now if you can??');
mediaplay_video.removeEventListener('timeupdate', function() {
alert('I am killed, but why am i again getting called???');
});
}
video_present('Terminator_10.webm');
The second argument to removeEventListener is the listener function itself. If you do not pass the same argument as with addEventListener, it will not be removed. Use a named function or a function variable to ensure the same function object is used in both places:
function handleTimeUpdate() {
var sss = parseInt(mediaplay_video.currentTime % 60);
show_second();
}
mediaplay_video.addEventListener('timeupdate', handleTimeUpdate, false);
...
mediaplay_video.removeEventListener('timeupdate', handleTimeUpdate);
I have a setinterval that runes every 5 seconds. this works fine on page load.
I have the following scenarios:
Load page with interval (WORKS)
press button and load new content and stopp interval(WORKS)
Once the new content is no longer desiered, dissmiss it, return to first content and start interval again(DOES NOT WORK)
I have saftys suchs as events for window.blur that also stops the interval so that the browser does not commponsate for all the missing intervals if i would change tabs or something. Keep in mind that step 3 did not work BUT if i would after step 3 change a tab and then return to my original page(execute blur) the interval would start working again.
NOTE all content loading here exept page load is done with ajax calls.
My code:
initializing:
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function() {
$.automation.tanks.tableInit();
});
binding function:
bindIntervalEvent: function (target, url, callback) {
$(window)
.on("focus.mine",
function() {
$.automation.worker.setUpdateInterval(target, url, callback);
})
.on("blur",
function() {
$.automation.worker.stopUpdateInterval();
}).trigger("focus.mine");
}
interval function:
setUpdateInterval: function (target, url, callback) {
if ($.automation.globals.globalInterval.value.length === 0) {
$.automation.globals.globalInterval.value.push(window.setInterval(
function () {
var options = {
loadTarget: target
}
$.automation.worker.getView(url,
function() {
if (callback)
callback();
},
options);
},
5000));
}
}
the function that stops the interval:
stopUpdateInterval: function () {
if ($.automation.globals.globalInterval.value.length === 0)
return;
console.log("deleting");
for (var i = 0; i <= $.automation.globals.globalInterval.value.length; i++) {
window.clearInterval($.automation.globals.globalInterval.value[i])
$.automation.globals.globalInterval.value.splice(i, 1);
console.log($.automation.globals.globalInterval.value.length);
}
}
when stopping the interval i also remove the window bindings:
unBindIntervalEvent: function() {
$(window).off("focus.mine");
$(window).unbind("blur");
}
Back to step 3:
My sucess method in the callback to my getviewfunction is identical to what i execute in the beginning
code:
$(".updatelatest")
.on("click",
function () {
var _this = $(this);
var options = {
loadTarget:"#TanksContent"
}
$.automation.worker.getView("/Tank/GetTanks",
function (data) {
$(_this).switchClass("col-md-5", "col-md-1", 1000, function() {
$(_this).addClass("hidden");
$(".search").switchClass("col-md-5", "col-md-12", 1000, "easeInOutQuad");
})
$.automation.tanks.tableInit();
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function () {
$.automation.tanks.tableInit();
});
$(window).trigger("blur");
}, options);
});
but this does not start the interval. it is clearly initialized since it works when window.blur is executed for example when I change tab but for some reason this is not working beyond that.
i tried triggering the windows blur event and nothing happened, i tried triggering my custom window event "focuse.mine" but nothing happens.
I did not notice this while developing since I had firebug open and every time i checked scripts or css or the console the blur function was executed so I assumed that my code worked as intended but now that it is deployed I notice this.
My head is pounding beyond reason and I can't for figure out where I have gone wrong.
Well this was a fun one. I simply found that when calling the setUpdateInterval(); function directly it gave me the desiered result.
I realized that the reason I had them split like I did was becaouse of the blur event. "Focus.mine" is triggered to start the inteval again ocne a user comes back to the page.
Why does goog.history.Html5History object fire goog.history.EventType.NAVIGATE event twice each time when fragment is changed? This is the example of code:
var history = goog.history.Html5History.isSupported()
? new goog.history.Html5History()
: new goog.History();
goog.events.listen(history, goog.history.EventType.NAVIGATE, function(e) {
console.log(['navigation', e.target.getToken()]);
});
history.setEnabled(true);
And this is log:
["navigation", "!/properties/new"]
["navigation", "!/properties/new"]
UPD: As I have figured out there are two different values of isNavigation field of e object in callback. First time it takes false value, and second time it takes true value. isNavigation means:
isNavigation True if the event was triggered by a browser action, such
as forward or back, clicking on a link, editing the URL, or calling
window.history.(go|back|forward). False if the token has been changed
by a setToken or replaceToken call.
But how to get only one even fired?
I met the same problem. But in my case both events has isNavigation==true.
init = function() {
var h = goog.history.Html5History.isSupported() ?
new goog.history.Html5History() : new goog.History();
goog.events.listen(h, goog.history.EventType.NAVIGATE, navigationCallback);
h.setEnabled(true);
};
navigationCallback = function(e) {
console.log(e, e.token, e.isNavigation);
};
// And then:
h.setToken("example1");
h.setToken("example2");
// And click "back" button in browser
Output:
goog.history.Event "example1" false
goog.history.Event "example2" false
goog.history.Event "example1" true
goog.history.Event "example1" true
I have the some problem..
Do google fired one time
http://closure-library.googlecode.com/git/closure/goog/demos/history1.html
Perhaps init happens twice?