Turn table contenteditable when tab key pressed - javascript

I am making a table entering tool, and I want to type items in when I press tab instead of having to click on each element. I was not able to figure out how to make that work, however.
$('table').on('mousedown objEvent.keyCode == 9', 'td', function(event) {
event = event || window.event;
var elementClass = $(this).attr('class');
if (elementClass != "noedit") {
$(event.target).closest('td').prop("contentEditable", true);
}
I've tried mousedown and tab key and only mousedown worked.
$('table').on('focus', 'td', function(event) {
event = event || window.event;
var elementClass = $(this).attr('class');
if (elementClass != "noedit") {
$(event.target).closest('td').prop("contentEditable", true);
}
Only mousedown worked this way as well.
Jsfiddle: https://jsfiddle.net/vLsw0goe/
Thank you.

try this:
$('table').on('mousedown', 'td:not(.noedit)', function(event) {
event = event || window.event;
$(event.target).closest('td').prop("contentEditable", true);
});
$('table').on('keydown', 'td:not(.noedit)', function(event) {
var i = $("td:not(.noedit)").index($(this));
$("td:not(.noedit)").eq(i+1).trigger("mousedown");
});

Related

When dragging file onto a HTML page, how do I stop them from opening up in full? [duplicate]

I'm adding an html5 drag and drop uploader to my page.
When a file is dropped into the upload area, everything works great.
However, if I accidentally drop the file outside of the upload area, the browser loads the local file as if it is a new page.
How can I prevent this behavior?
Thanks!
You can add a event listener to the window that calls preventDefault() on all dragover and drop events.
Example:
window.addEventListener("dragover",function(e){
e = e || event;
e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
},false);
After a lot of fiddling around, I found this to be the stablest solution:
var dropzoneId = "dropzone";
window.addEventListener("dragenter", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
}, false);
window.addEventListener("dragover", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
});
window.addEventListener("drop", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
});
<div id="dropzone">...</div>
Setting both effectAllow and dropEffect unconditionally on the window causes my drop zone not to accept any d-n-d any longer, regardless whether the properties are set new or not.
To allow drag-and-drop only on some elements, you could do something like:
window.addEventListener("dragover",function(e){
e = e || event;
console.log(e);
if (e.target.tagName != "INPUT") { // check which element is our target
e.preventDefault();
}
},false);
window.addEventListener("drop",function(e){
e = e || event;
console.log(e);
if (e.target.tagName != "INPUT") { // check which element is our target
e.preventDefault();
}
},false);
For jQuery the correct answer will be:
$(document).on({
dragover: function() {
return false;
},
drop: function() {
return false;
}
});
Here return false will behave as event.preventDefault() and event.stopPropagation().
Note: Although the OP did not ask for an Angular solution, I came here looking for that. So this is to share what I found to be a viable solution, if you use Angular.
In my experience this problem first arises when you add file drop functionality to a page. Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone.
In my solution the drop zone is an input with a class, but any unambiguous selector works.
import { Component, HostListener } from '#angular/core';
//...
#Component({
template: `
<form>
<!-- ... -->
<input type="file" class="dropzone" />
</form>
`
})
export class MyComponentWithDropTarget {
//...
#HostListener('document:dragover', ['$event'])
#HostListener('drop', ['$event'])
onDragDropFileVerifyZone(event) {
if (event.target.matches('input.dropzone')) {
// In drop zone. I don't want listeners later in event-chain to meddle in here
event.stopPropagation();
} else {
// Outside of drop zone! Prevent default action, and do not show copy/move icon
event.preventDefault();
event.dataTransfer.effectAllowed = 'none';
event.dataTransfer.dropEffect = 'none';
}
}
}
The listeners are added/removed automatically when component is created/destroyed, and other components using the same strategy on the same page do not interfere with each other due to the stopPropagation().
Here's a little more modernized version of this answer using ES6 syntax.
let dropzoneId = 'dropzone'
const dragEventHandler = e => {
if (e.target.id !== dropzoneId) {
e.preventDefault
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
// window.addEventListener("dragenter", dragEventHandler, false)
// window.addEventListener("dragover", dragEventHandler, false)
// window.addEventListener("drop", dragEventHandler, false)
['dragenter', 'dragover', 'drop'].forEach(ev => window.addEventListener(ev, dragEventHandler, false))
<div id="dropzone">...</div>
try this:
document.body.addEventListener('drop', function(e) {
e.preventDefault();
}, false);
Preventing all drag and drop operations by default might not be what you want. It's possible to check if the drag source is an external file, at least in some browsers. I've included a function to check if the drag source is an external file in this StackOverflow answer.
Modifying Digital Plane's answer, you could do something like this:
function isDragSourceExternalFile() {
// Defined here:
// https://stackoverflow.com/a/32044172/395461
}
window.addEventListener("dragover",function(e){
e = e || event;
var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
if (IsFile) e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
if (IsFile) e.preventDefault();
},false);
To build on the "check the target" method outlined in a few other answers, here is a more generic/functional method:
function preventDefaultExcept(predicates) {
return function (e) {
var passEvery = predicates.every(function (predicate) { return predicate(e); })
if (!passEvery) {
e.preventDefault();
}
};
}
Called like:
function isDropzone(e) { return e.target.id === 'dropzone'; }
function isntParagraph(e) { return e.target.tagName !== 'p'; }
window.addEventListener(
'dragover',
preventDefaultExcept([isDropzone, isntParagraph])
);
window.addEventListener(
'drop',
preventDefaultExcept([isDropzone])
);
I have an HTML object (embed) that fills the width and height of the page. The answer by #digital-plane works on normal web pages but not if the user drops onto an embedded object. So I needed a different solution.
If we switch to using the event capture phase we can get the events before the embedded object receives them (notice the true value at the end of the event listener call):
// document.body or window
document.body.addEventListener("dragover", function(e){
e = e || event;
e.preventDefault();
console.log("over true");
}, true);
document.body.addEventListener("drop", function(e){
e = e || event;
e.preventDefault();
console.log("drop true");
}, true);
Using the following code (based on #digital-plane's answer) the page becomes a drag target, it prevents object embeds from capturing the events and then loads our images:
document.body.addEventListener("dragover", function(e){
e = e || event;
e.preventDefault();
console.log("over true");
}, true);
document.body.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
console.log("Drop true");
// begin loading image data to pass to our embed
var droppedFiles = e.dataTransfer.files;
var fileReaders = {};
var files = {};
var reader;
for (var i = 0; i < droppedFiles.length; i++) {
files[i] = droppedFiles[i]; // bc file is ref is overwritten
console.log("File: " + files[i].name + " " + files[i].size);
reader = new FileReader();
reader.file = files[i]; // bc loadend event has no file ref
reader.addEventListener("loadend", function (ev, loadedFile) {
var fileObject = {};
var currentReader = ev.target;
loadedFile = currentReader.file;
console.log("File loaded:" + loadedFile.name);
fileObject.dataURI = currentReader.result;
fileObject.name = loadedFile.name;
fileObject.type = loadedFile.type;
// call function on embed and pass file object
});
reader.readAsDataURL(files[i]);
}
}, true);
Tested on Firefox on Mac.
I am using a class selector for multiple upload areas so my solution took this less pure form
Based on Axel Amthor's answer, with dependency on jQuery (aliased to $)
_stopBrowserFromOpeningDragAndDropPDFFiles = function () {
_preventDND = function(e) {
if (!$(e.target).is($(_uploadBoxSelector))) {
e.preventDefault();
e.dataTransfer.effectAllowed = 'none';
e.dataTransfer.dropEffect = 'none';
}
};
window.addEventListener('dragenter', function (e) {
_preventDND(e);
}, false);
window.addEventListener('dragover', function (e) {
_preventDND(e);
});
window.addEventListener('drop', function (e) {
_preventDND(e);
});
},
For what its worth, I use the following. Nice and explicit if not particularly elegant perhaps?
var myDropZone = document.getElementById('drop_zone');
// first, inhibit the default behaviour throughout the window
window.addEventListener('drop', () => {
event.preventDefault();
} );
window.addEventListener('dragover', () => {
event.dataTransfer.dropEffect = 'none'; // dont allow drops
event.preventDefault();
} );
// Next, allow the cursor to show 'copy' as it is dragged over
// my drop zone but dont forget to stop the event propagating
myDropZone.addEventListener('dragover', () => {
event.dataTransfer.dropEffect = 'copy';
event.stopPropagation(); // important !!
event.preventDefault();
} );
// In my drop zone, deal with files as they are dropped
myDropZone.addEventListener('drop', myDropHandler);

AddEventListener on every links and childs

Is it possible to attach a new event to every links of a page recursively? Currently I'm using the following code:
var linkHandler = function (e) {
e = e || window.event;
var element = e.target || e.srcElement;
console.log("Element clicked, type: "+element.tagName);
if (element.tagName == 'A') {
console.log("Catched "+element.href);
element.href = url(element.href);
}
};
document.addEventListener("click", linkHandler, true);
But it only works if I directly click on a link. For example here, only the click on "Work" of the second link works.
<strong>Doesn't work</strong>
Work <strong>Doesn't</strong>`
Is there another possibility ?

Prevent browser from loading a drag-and-dropped file

I'm adding an html5 drag and drop uploader to my page.
When a file is dropped into the upload area, everything works great.
However, if I accidentally drop the file outside of the upload area, the browser loads the local file as if it is a new page.
How can I prevent this behavior?
Thanks!
You can add a event listener to the window that calls preventDefault() on all dragover and drop events.
Example:
window.addEventListener("dragover",function(e){
e = e || event;
e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
},false);
After a lot of fiddling around, I found this to be the stablest solution:
var dropzoneId = "dropzone";
window.addEventListener("dragenter", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
}, false);
window.addEventListener("dragover", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
});
window.addEventListener("drop", function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.effectAllowed = "none";
e.dataTransfer.dropEffect = "none";
}
});
<div id="dropzone">...</div>
Setting both effectAllow and dropEffect unconditionally on the window causes my drop zone not to accept any d-n-d any longer, regardless whether the properties are set new or not.
To allow drag-and-drop only on some elements, you could do something like:
window.addEventListener("dragover",function(e){
e = e || event;
console.log(e);
if (e.target.tagName != "INPUT") { // check which element is our target
e.preventDefault();
}
},false);
window.addEventListener("drop",function(e){
e = e || event;
console.log(e);
if (e.target.tagName != "INPUT") { // check which element is our target
e.preventDefault();
}
},false);
For jQuery the correct answer will be:
$(document).on({
dragover: function() {
return false;
},
drop: function() {
return false;
}
});
Here return false will behave as event.preventDefault() and event.stopPropagation().
Note: Although the OP did not ask for an Angular solution, I came here looking for that. So this is to share what I found to be a viable solution, if you use Angular.
In my experience this problem first arises when you add file drop functionality to a page. Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone.
In my solution the drop zone is an input with a class, but any unambiguous selector works.
import { Component, HostListener } from '#angular/core';
//...
#Component({
template: `
<form>
<!-- ... -->
<input type="file" class="dropzone" />
</form>
`
})
export class MyComponentWithDropTarget {
//...
#HostListener('document:dragover', ['$event'])
#HostListener('drop', ['$event'])
onDragDropFileVerifyZone(event) {
if (event.target.matches('input.dropzone')) {
// In drop zone. I don't want listeners later in event-chain to meddle in here
event.stopPropagation();
} else {
// Outside of drop zone! Prevent default action, and do not show copy/move icon
event.preventDefault();
event.dataTransfer.effectAllowed = 'none';
event.dataTransfer.dropEffect = 'none';
}
}
}
The listeners are added/removed automatically when component is created/destroyed, and other components using the same strategy on the same page do not interfere with each other due to the stopPropagation().
Here's a little more modernized version of this answer using ES6 syntax.
let dropzoneId = 'dropzone'
const dragEventHandler = e => {
if (e.target.id !== dropzoneId) {
e.preventDefault
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
// window.addEventListener("dragenter", dragEventHandler, false)
// window.addEventListener("dragover", dragEventHandler, false)
// window.addEventListener("drop", dragEventHandler, false)
['dragenter', 'dragover', 'drop'].forEach(ev => window.addEventListener(ev, dragEventHandler, false))
<div id="dropzone">...</div>
try this:
document.body.addEventListener('drop', function(e) {
e.preventDefault();
}, false);
Preventing all drag and drop operations by default might not be what you want. It's possible to check if the drag source is an external file, at least in some browsers. I've included a function to check if the drag source is an external file in this StackOverflow answer.
Modifying Digital Plane's answer, you could do something like this:
function isDragSourceExternalFile() {
// Defined here:
// https://stackoverflow.com/a/32044172/395461
}
window.addEventListener("dragover",function(e){
e = e || event;
var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
if (IsFile) e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
if (IsFile) e.preventDefault();
},false);
To build on the "check the target" method outlined in a few other answers, here is a more generic/functional method:
function preventDefaultExcept(predicates) {
return function (e) {
var passEvery = predicates.every(function (predicate) { return predicate(e); })
if (!passEvery) {
e.preventDefault();
}
};
}
Called like:
function isDropzone(e) { return e.target.id === 'dropzone'; }
function isntParagraph(e) { return e.target.tagName !== 'p'; }
window.addEventListener(
'dragover',
preventDefaultExcept([isDropzone, isntParagraph])
);
window.addEventListener(
'drop',
preventDefaultExcept([isDropzone])
);
I have an HTML object (embed) that fills the width and height of the page. The answer by #digital-plane works on normal web pages but not if the user drops onto an embedded object. So I needed a different solution.
If we switch to using the event capture phase we can get the events before the embedded object receives them (notice the true value at the end of the event listener call):
// document.body or window
document.body.addEventListener("dragover", function(e){
e = e || event;
e.preventDefault();
console.log("over true");
}, true);
document.body.addEventListener("drop", function(e){
e = e || event;
e.preventDefault();
console.log("drop true");
}, true);
Using the following code (based on #digital-plane's answer) the page becomes a drag target, it prevents object embeds from capturing the events and then loads our images:
document.body.addEventListener("dragover", function(e){
e = e || event;
e.preventDefault();
console.log("over true");
}, true);
document.body.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
console.log("Drop true");
// begin loading image data to pass to our embed
var droppedFiles = e.dataTransfer.files;
var fileReaders = {};
var files = {};
var reader;
for (var i = 0; i < droppedFiles.length; i++) {
files[i] = droppedFiles[i]; // bc file is ref is overwritten
console.log("File: " + files[i].name + " " + files[i].size);
reader = new FileReader();
reader.file = files[i]; // bc loadend event has no file ref
reader.addEventListener("loadend", function (ev, loadedFile) {
var fileObject = {};
var currentReader = ev.target;
loadedFile = currentReader.file;
console.log("File loaded:" + loadedFile.name);
fileObject.dataURI = currentReader.result;
fileObject.name = loadedFile.name;
fileObject.type = loadedFile.type;
// call function on embed and pass file object
});
reader.readAsDataURL(files[i]);
}
}, true);
Tested on Firefox on Mac.
I am using a class selector for multiple upload areas so my solution took this less pure form
Based on Axel Amthor's answer, with dependency on jQuery (aliased to $)
_stopBrowserFromOpeningDragAndDropPDFFiles = function () {
_preventDND = function(e) {
if (!$(e.target).is($(_uploadBoxSelector))) {
e.preventDefault();
e.dataTransfer.effectAllowed = 'none';
e.dataTransfer.dropEffect = 'none';
}
};
window.addEventListener('dragenter', function (e) {
_preventDND(e);
}, false);
window.addEventListener('dragover', function (e) {
_preventDND(e);
});
window.addEventListener('drop', function (e) {
_preventDND(e);
});
},
For what its worth, I use the following. Nice and explicit if not particularly elegant perhaps?
var myDropZone = document.getElementById('drop_zone');
// first, inhibit the default behaviour throughout the window
window.addEventListener('drop', () => {
event.preventDefault();
} );
window.addEventListener('dragover', () => {
event.dataTransfer.dropEffect = 'none'; // dont allow drops
event.preventDefault();
} );
// Next, allow the cursor to show 'copy' as it is dragged over
// my drop zone but dont forget to stop the event propagating
myDropZone.addEventListener('dragover', () => {
event.dataTransfer.dropEffect = 'copy';
event.stopPropagation(); // important !!
event.preventDefault();
} );
// In my drop zone, deal with files as they are dropped
myDropZone.addEventListener('drop', myDropHandler);

How to implement jQuery's .not()?

I have code like:
document.onmousedown = function(){
alert('test');
}
Now, except the element with ID "box", clicking should call this function, i.e. the equivalent of jQuery's .not() selector.
The jQuery code would be:
$(document).not('#box').mousedown(function(){
alert('test');
});
How can I achieve the same thing without using jQuery?
Edit: I don't want jQuery code, but i want an action similar to the .not() selector of jQuery in Javascript.
Edit: I am making an addthis-like widget. It is a 10kb file which will show a popup when a text is selected. It will not use jQuery.
In my case, when a text is selected, a popup is shown. When the document is clicked somewhere other than the widget, the widget should disappear.
To do this properly, you need to check whether e.target || e.srcElement or any of its parents has id === 'box'.
For example: (with jQuery)
$(document).mousedown(function(e) {
if ($(e.target).closest('#box').length)
return;
//Do things
});
Without jQuery:
function isBox(elem) {
return elem != null && (elem.id === 'box' || isBox(elem.parentNode));
}
document.onmousedown = function(e) {
e = e || window.event;
if (isBox(e.target || e.srcElement))
return;
//Do things
};
Alternatively, you could handle the mousedown event for the box element and cancel bubbling.
Here's one way that should work:
document.onmousedown = function(e){
var event = e || window.event;
var element = event.target || event.srcElement;
if (target.id !== "box") { alert("hi"); }
}
or if you would like it to be reusable with different ids:
function myNot(id, callback) {
return function (e) {
var event = e || window.event;
var element = event.target || event.srcElement;
if (target.id !== id) { callback(); }
}
}
and to use it:
document.onmousedown = myNot("box", function () {
alert("hi");
});
The cleanest way I can come up with for what you're trying to do is to set a document.onmousedown event and then halt event propagation on the box.onmousedown event. This avoids creating a large number of onmousedown events all over the document, and avoids having to recurse through the entire parent hierarchy of a node every time an event is triggered.
document.onmousedown = function() {
alert("Foo!");
};
document.getElementById("box").onmousedown = function(e) {
alert("Bar!");
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
};

how to check when field enter/return has been pressed on?

I have multiple fields, typically enter will be pressed on one of the two main ones. I want to know which field enter has been pressed on, how do i do this? (i dont know much JS)
its simple to add an "onkeypress" event to each of the fields, and then in the event handler to examine the keycode that is attached to the event. For example, consider the following code:
form.elements['fieldone'].onkeypress = function(evt) {
if (window.event) evt = window.event; // support IE
if (evt.keyCode == 13) alert("Enter was pressed!");
return true;
}
Please note that under most browsers, pressing ENTER in a form field would post that form. If you don't want that to happen, you can simply return false from the onkeypress handler and that would tell the browser to ignore that key.
Check for enter and set some hidden field (example uses JQuery):
$('#input_text').keyup(function(e) {
//alert(e.keyCode);
if(e.keyCode == 13) {
alert('Enter key was pressed.');
}
});
Include this in your page, it should fire automatically when you hit any key and tell you which html element had focus when it happened.
<script>
document.onkeypress = KeyPressed;
function KeyPressed(e)
{
if (!e) e = window.event;
f ((e.charCode) && (e.keyCode == 13))
alert('Yay! Enter was pressed while field ' + document.activeElement.id + ' had focus!');
}
</script>
You can check from which element the event bubbled from using something like the following
var text1 = document.getElementById('text1');
var text2 = document.getElementById('text2');
text1.onkeypress = keyPresser;
text2.onkeypress = keyPresser;
function keyPresser(e) {
// to support IE event model
var e = e || window.event;
var originalElement = e.srcElement || e.originalTarget;
if (e.keyCode === 13) {
alert(originalElement.id);
}
}
Here's a Working Demo
I would recommend taking a look at the differences in Browser event models and also at unobtrusive JavaScript .
QuirksMode - Introduction to Events
The IE Event Model
Pro JavaScript Techniques - Unobtrusive Event Binding
Use event delegation to avoid attaching event handlers to a large number of elements:
window.onload = function () {
document.onkeyup = function (e) {
e = e || window.event;
var target = e.target || e.srcElement,
keyCode = e.keyCode || e.which;
if (target.tagName.toLowerCase() == 'input' && keyCode == 13) {
alert('Enter pressed on ' + target.id);
}
};
};

Categories