I need your help, again
I can drag and drop a file or click on a basic input file on HTML5 and it'll work.
What I want is to be able to make that with a label, I can click on the label to add a file with a label for but I can't drag and drop a file on the label. I've tried to make it possible with that JavaScript over here but it didn't work, I've read some tutorials and I'm not sure of the code below.
$(document).on('dragenter', '#image-event-label', function() {
$(this).css('border', '1px solid #B8A1F5');
return false;
});
$(document).on('dragover', '#image-event-label', function(e){
e.preventDefault();
e.stopPropagation();
$(this).css('border', '1px solid #B8A1F5');
return false;
});
$(document).on('dragleave', '#image-event-label', function(e) {
e.preventDefault();
e.stopPropagation();
$(this).css('border', '1px solid #422B7E');
return false;
});
$(document).on('drop', '#image-event-label', function(e) {
if(e.originalEvent.dataTransfer){
if(e.originalEvent.dataTransfer.files.length) {
e.preventDefault();
e.stopPropagation();
$(this).css('border', '1px solid #0F0');
upload(e.originalEvent.dataTransfer.files);
}
}
else {
$(this).css('border', '1px solid #422B7E');
}
return false;
});
function upload(files) {
var f = files[0] ;
var reader = new FileReader();
reader.onload = function (event) {
$('#image-event').val(event.target.result);
}
reader.readAsDataURL(f);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="image-event" id="image-event-label">Import an image</label>
<input type="file" name="image-event" style="display:none" id="image-event">
Thanks everybody in advance !
The easiest way, is to append your input[type=file] directly in your <label>, and to style this input in a way it covers all the label.
Doing so, you'll be able to drop files directly on the label, and the input's default behavior will take care of it, ultimately, no js is needed in this part:
// only to show it did change
$('#image-event').on('change', function upload(evt) {
console.log(this.files[0]);
});
// only to show where is the drop-zone:
$('#image-event-label').on('dragenter', function() {
this.classList.add('dragged-over');
})
.on('dragend drop dragexit dragleave', function() {
this.classList.remove('dragged-over');
});
#image-event {
position: absolute;
top: 0;
left: 0;
opacity: 0;
width: 100%;
height: 100%;
display: block;
}
#image-event-label {
position: relative;
}
#image-event-label.dragged-over {
border: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="image-event" id="image-event-label">
Import an image
<input type="file" name="image-event" id="image-event">
</label>
Now, note that it is actually now* possible to set the .files FileList of an input[type=file], but you need to set it to an other FileList object, and fortunately, there is one available in the DataTransfer object that comes along the DropEvent:
function handleDroppedFile(evt) {
evt.preventDefault();
// since we use jQuery we need to grab the originalEvent
var dT = evt.originalEvent.dataTransfer;
var files = dT.files;
if (files && files.length) {
// we set our input's 'files' property
$('#image-event')[0].files = files;
}
}
$('#image-event-label').on({
'drop': handleDroppedFile,
'dragenter': function(e) { e.preventDefault(); },
'dragover': function(e) {
e.preventDefault();
this.classList.add('dragged-over');
}
})
.on('dragleave dragexit', function() {
this.classList.remove('dragged-over')
});
.dragged-over {
border: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="image-event" id="image-event-label">
Drop a file here
</label> <br><br>
<input type="file" name="image-event" id="image-event">
*IIRC, this is now supported in Chrome, Safari, latests Edge and latests Firefox. I don't think IE does support it though, so beware when using this.
Related
In IE 11 if I have an empty email input with a placeholder, then on clicking (focusing) it, the input event is being triggered.
Does anyone know why and is there a solution to this, since the input value hasn't really changed?
var el = document.getElementById('myEmail');
el.addEventListener("input", myFunction, true);
function myFunction()
{
alert("changed");
}
<input id="myEmail" type="email" placeholder="Email">
I came very late to the party, but I had the same problem, and I came to a workaround to fix this behavior on IE.
In fact, there's two different bugs (or rather, only one bug but with two behavior depending on whether the target is an input or a textarea).
For input : the event is triggered each time the visual content of the field change, including keyboard inputting (naturally), but also when a placeholder appears/disappears (blur when no content), or when a visible placeholder is changed programmatically.
For textarea: it's basically the same, exepts that the event don't trigger when the placeholder disapears.
function onInputWraper(cb) {
if (!window.navigator.userAgent.match(/MSIE|Trident/)) return cb;
return function (e) {
var t = e.target,
active = (t == document.activeElement);
if (!active || (t.placeholder && t.composition_started !== true)) {
t.composition_started = active;
if ((!active && t.tagName == 'TEXTAREA') || t.tagName == 'INPUT') {
e.stopPropagation();
e.preventDefault();
return false;
}
}
cb(e);
};
}
var el = document.getElementById('myEmail');
el.addEventListener("input", onInputWraper(myFunction), true);
function myFunction() {
alert("changed");
}
<input id="myEmail" type="email" placeholder="Email">
And there's a full-working example, where you can also change placeholders value
function onInputWraper(cb) {
if (!window.navigator.userAgent.match(/MSIE|Trident/)) return cb;
return function (e) {
var t = e.target,
active = (t == document.activeElement);
if (!active || (t.placeholder && t.composition_started !== true)) {
t.composition_started = active;
if ((!active && t.tagName == 'TEXTAREA') || t.tagName == 'INPUT') {
e.stopPropagation();
e.preventDefault();
return false;
}
}
cb(e);
};
}
function handle(event) {
console.log('EVENT', event);
document.getElementById('output')
.insertAdjacentHTML('afterbegin', "<p>" + event.type + " triggered on " + event.target.tagName +
'</p>');
}
var input = document.getElementById('input'),
textarea = document.getElementById('textarea');
input.addEventListener('input', onInputWraper(handle));
textarea.addEventListener('input', onInputWraper(handle));
// input.addEventListener('input', handle);
// textarea.addEventListener('input', handle);
// Example's settings
function removeListeners(elem) {
var value = elem.value,
clone = elem.cloneNode(true);
elem.parentNode.replaceChild(clone, elem);
clone.value = value;
return clone;
}
document.querySelector('#settings input[type="checkbox"]').addEventListener('change', function (event) {
if (event.target.checked) {
document.getElementById('output').insertAdjacentHTML('afterbegin', '<p>Filter enabled !</p>');
//input = removeListeners(input);
console.log(input.value.length, (input == document.activeElement));
input = removeListeners(input);
input.addEventListener('input', onInputWraper(handle));
input.composing = input.value.length > 0 || (input == document.activeElement);
textarea = removeListeners(textarea);
textarea.addEventListener('input', onInputWraper(handle));
textarea.composing = textarea.value.length > 0 || (textarea == document.activeElement);
} else {
document.getElementById('output').insertAdjacentHTML('afterbegin', '<p>Filter disabled !</p>');
input = removeListeners(input);
input.addEventListener('input', handle);
input.composing = void 0;
textarea = removeListeners(textarea);
textarea.addEventListener('input', handle);
textarea.composing = void 0;
}
});
document.getElementById('input_cfg').addEventListener('click', function () {
document.getElementById('input').setAttribute('placeholder', document.getElementById(
'input_placeholder').value);
});
document.getElementById('textarea_cfg').addEventListener('click', function () {
document.getElementById('textarea').setAttribute('placeholder', document.getElementById(
'textarea_placeholder').value);
});
* {
font: 15px arial, sans-serif;
}
dd {
background: FloralWhite;
margin: 0;
}
dt {
padding: 15px;
font-size: 1.2em;
background: steelblue;
color: AntiqueWhite;
}
p {
margin: 0;
}
button,
label {
width: 300px;
margin: 5px;
padding: 5px;
float: left;
color: DarkSlateGray;
}
#settings label {
width: 100%;
margin: 15px;
}
#forms input,
#forms textarea,
#settings input:not([type]) {
display: block;
width: calc(100% - 340px);
padding: 7px;
margin: 0;
margin-left: 320px;
min-height: 25px;
border: 1px solid gray;
background: white;
cursor: text;
}
::placeholder {
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: LightBlue;
opacity: 1;
/* Firefox */
}
::-ms-input-placeholder {
/* Microsoft Edge */
color: LightBlue;
}
:-ms-input-placeholder {
/* Internet Explorer 10-11 */
color: LightBlue;
}
<dl>
<dt>Forms</dt>
<dd id="forms">
<label for="input">Input: </label>
<input id="input" name="input" class="testing" placeholder="Type some text" />
<label for="texarea">Textarea: </label>
<textarea id="textarea" name="textarea" placeholder="Type some text"></textarea>
</dd>
<dt>Settings</dt>
<dd id="settings">
<p>
<label><input type="checkbox" checked>Enable filtering script</label>
<button id="input_cfg">Change input's placeholder to</button><input id="input_placeholder" />
</p>
<p>
<button id="textarea_cfg">Change textarea's placeholder to</button>
<input id="textarea_placeholder" />
</p>
</dd>
<dt>Output</dt>
<dd id="output"></dd>
</dl>
or on jsfiddle
It seems like a bug. oninput has been supported since IE9 should only fire when the value is changed. An alternate approach would be to use onkeyup
https://developer.mozilla.org/en-US/docs/Web/Events/input
If you want to handle input and validation you can just add a second eventlistener (assuming your html is the same as above).
var el = document.getElementById('myEmail');
function myFunction() {
console.log("changed");
}
el.addEventListener("keyup", myFunction, true);
function validation() {
console.log("validated");
}
el.addEventListener("keyup", validation, true);
The solution is very short!
At first we don't need so much of code what we can see in accepted answer. At second you have to understand why it is happening:
Fake oninput event triggers only on inputs with a placeholder on getting a focus and only if the input value is empty. It happens because bad developer from IE had thought that the input value changes from placeholder value to real value, but it is misunderstanding from this bad developer from IE.
An input without placeholders doesn't have this problems.
The first solution is very simple: do not use a placeholder if you do not really need it. In most cases you can add a title to the element instead of a placeholder. You could also write your placeholder text before, below or above the input.
If you have to use a placeholder
The second solution is also very simple and short: save the previous value on the input object and compare it with a new value – if it was not changed then return from the function before your code.
Life examples
var input = document.querySelector('input[type=search]');
input.addEventListener('input', function(e)
{
if(this.prevVal == this.value /* if the value was not changed */
|| !this.prevVal && '' == this.value) //only for the first time because we do not know the previous value
return; //the function returns "undefined" as value in this case
this.prevVal = this.value;
//YOUR CODE PLACE
console.log('log: ' + this.value)
}, false);
<input type="search" placeholder="Search..."/>
Or if you use it as inline JavaScript:
function onSearch(trg)
{
if(trg.prevVal == trg.value /* if the value was not changed */
|| !trg.prevVal && '' == trg.value) //only for the first time because we do not know the previous value
return; //the function returns "undefined" as value in this case
trg.prevVal = trg.value; // save the previous value on input object
//YOUR CODE PLACE
console.log('log: ' + trg.value);
}
<input type="search" placeholder="Search..." oninput="onSearch(this)"/>
I'm trying to catch a click event on an element which changes its z-pos using appendchild on the mousedown event. The problem is that when you click an element when its not the front element then the click event doesn't fire. I know this is because it is removed from the DOM and then re-added but I'm not sure how I could fix it so that the click also fire when the element is moved to the front.
MyObject = {
init(parent, text) {
this.parent = parent;
this.text= text;
this.visual = document.createElement("div");
this.visual.setAttribute("class", "object");
this.visual.appendChild(document.createTextNode(text))
parent.appendChild(this.visual);;
this.visual.addEventListener("click", (e) => { this.onClicked(e); });
this.visual.addEventListener("mousedown", (e) => { this.onMouseDown (e); });
},
toTop() {
this.parent.appendChild(this.visual);
},
onClicked(e) {
alert(this.text + " was clicked");
e.stopPropagation();
},
onMouseDown(e) {
this.toTop();
// I'm also doing other things here
}
};
var parent = document.querySelector(".parent");
parent.addEventListener("click", (e) => { alert("parent clicked"); });
var createObj = function(text) {
var obj = Object.create(MyObject);
obj.init (parent, text);
};
createObj ("object 1");
createObj ("object 2");
createObj ("object 3");
createObj ("object 4");
.parent {
border: 1px solid red;
}
.object {
background: #0F0;
border: 1px solid black;
margin: 8px;
}
<div class="parent">
</div>
So in this example you always have to click the bottom element to get the alert while I would also like to get the alert when an other items is pressed.
edit: I'm testing in chrome (55.0.2883.87) and the code might not work in other browsers.
This seems like a simple thing to do, but I have not been able to find anything about this.
How can I use something like the following:
// html: <input type="text" onchange="validate()">
function validate(e) {
e.preventDefault();
if(isValid(this.value)) {
// make valid somehow
} else {
// make invalid somehow
}
}
so that the following CSS works as you might expect:
input:valid {
background: green;
}
input:invalid {
background: red;
}
Click "Run code snippet" to see!
You can create a custom validator and use the setCustomValidity function on the element to allow use of these selectors.
This article describes how to use the HTML5 Constraint API to achieve this.
Example:
#inputField:valid {
background-color: green;
}
#inputField:invalid {
background-color: red;
}
<html>
<body>
Type a value (must be 'abc'): <input id="inputField">
<script type="text/javascript">
function validateField() {
if (this.value !== 'abc') {
this.setCustomValidity('Value must be abc!');
}
else {
this.setCustomValidity('');
}
}
window.onload = function () {
document.getElementById("inputField").oninput= validateField;
}
</script>
</body>
</html>
function validateField(event) {
event.preventDefault();
var target = event.currentTarget
if (target.value != "" || target.value.length > 0) {
target.classList.remove("invalidFunction");
target.classList.add("validFunction");
} else {
target.classList.add("invalidFunction");
target.classList.remove("validFunction");
}
};
.invalidFunction {
border-color: red;
}
.validFunction{
border-color: green;
}
Field is mandatory:
<input id="inputFunctionField" onchange="validateField(event)">
you can just use css classes if you wish (and I added the syntax for using jquery to addclasses and remove classes but can also be done in pure javascript)..
input.valid {
background: green;
}
input.invalid {
background: red;
}
function validate(e) {
e.preventDefault();
if(isValid(this.value)) {
$(this).removeClass("invalid");
$(this).addClass("valid");
} else {
$(this).removeClass("valid");
$(this).addClass("invalid");
// make invalid somehow
}
}
none jquery (assuming there is no other classes currently used. as this seems like the html you have, if there are additional classes just manipulate the string to add and remove the relevant class).
function validate(e) {
e.preventDefault();
if(isValid(this.value)) {
this.className = "valid"
} else {
this.className = "invalid"
// make invalid somehow
}
}
Are you looking for something like this?
// html: <input type="text" onchange="validate()">
function validate(e) {
e.preventDefault();
var target = e.target
if(isValid(this.value)) {
target.classList.add("valid")
target.classList.remove("invalid")
} else {
target.classList.remove("valid")
target.classList.add("invalid")
}
}
I'm having issues trying to resize a text area, I can do it as the user is typing but when they have submit it this gets put into a database and put into a text area below and display as a message on a message board but if the message exceeds the size of the Text Area it's not displayed I was wondering if anyone out there has had this issue and overcome it.
Here is the solution I came up with for the resizing whilst typing,
function resizeTextarea (id) {
var a = document.getElementById(id);
a.style.height = 'auto';
a.style.height = a.scrollHeight+'px';
}
function init()
{
var a = document.getElementsByTagName('textarea');
for(var i=0,inb=a.length;i<inb;i++)
{
if(a[i].getAttribute('data-resizable')=='true')
{
resizeTextarea(a[i].id);
}
}
}
addEventListener('DOMContentLoaded', init);
This is called on keyup on the textarea in my other page where ive used it but I have tried to do something like this to resize when it loads but it doesn't work but it does work when a key is pressed or a button is clicked.
onload="resizeTextarea('commentstext');"
I know i could always have it scrollable or put it into a div but divs don't format the text like a textarea if i do a line break in the text and submit it to a div ti wont be there
See my modification of prior solution: http://jsfiddle.net/CbqFv/570/
HTML
<textarea cols="42" rows="1">
1 ...by default, you can write more rows</textarea><br />
<br />
<textarea cols="42" rows="5">
1
2
3
4
5 ...by default, you can write more rows</textarea><br />
<br />
<textarea cols="42" rows="10">
1
2
3
4
5
6
7
8
9
10 ...by default, you can write more rows</textarea>
CSS
textarea {
border: 1px solid gray;
border-radius: 3px;
line-height: 1.3em;
overflow: hidden;
padding: 0.3em 0.3em 0 0.3em;
outline: none;
background-color: white;
resize: none;
}
JavaScript
var observe;
if (window.attachEvent) {
observe = function (element, event, handler) {
element.attachEvent('on'+event, handler);
};
}
else {
observe = function (element, event, handler) {
element.addEventListener(event, handler, false);
};
}
function init () {
function resize (element) {
element.style.height = 'auto';
element.style.height = element.scrollHeight+'px';
}
/* 0-timeout to get the already changed text */
function delayedResize (element) {
window.setTimeout(function() { resize(element) }, 0);
}
var textareas = document.getElementsByTagName("textarea");
for (i = 0; i < textareas.length; i++) {
var textarea = textareas[i];
observe(textarea, 'change', function() { resize(this) });
observe(textarea, 'cut', function() { delayedResize(this) });
observe(textarea, 'paste', function() { delayedResize(this) });
observe(textarea, 'drop',function() { delayedResize(this) });
observe(textarea, 'keydown', function() { delayedResize(this) });
textarea.focus();
textarea.select();
resize(textarea);
}
}
init();
from autosize documentation:
Autosize has no way of knowing when the value of a textarea has been
changed through JavaScript. If you do this, trigger the
autosize.resize event immediately after to update the height. Example:
$('#example').val('New Text!').trigger('autosize.resize');
So after you update the textarea content, you'll have to call that trigger to autoresize it
I'm using html5's drag and drop functionalities to rearrange dom elements on screen - I attach css behavior to the various states of dragging and dropping when I do this, but the problem I'm experiencing is that the hover state remains even after I've dragged, dropped, and moused out of a DOM element. Here's my code:
JAVASCRIPT:
function addDragListeners(){
$('.segmentListItem').each(function(index){
$(this)[0].addEventListener('dragstart',handleDragStart,false); //rollover for current
$(this)[0].addEventListener('drop',handleDrop,false); //drops dragged element
$(this)[0].addEventListener('dragover',handleDragOver,false); //allows us to drop
$(this)[0].addEventListener('dragenter',handleDragEnter,false); //rollover for target
$(this)[0].addEventListener('dragleave',handleDragLeave,false); //sets dragged item back to normal
$(this)[0].addEventListener('dragend',handleDragEnd,false); //sets all back to normal
});
}
function handleDragEnter(e) {
// this / e.target is the current hover target.
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over'); // this / e.target is previous target element.
}
function handleDragEnd(e){
$('.segmentListItem').removeClass('over'); //removes the over class on all elements
}
function handleDragStart(e){
draggedItem = this;
e.dataTransfer.effectAllowed = 'move';
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object.
return false;
}
function handleDrop(e){
if (e.stopPropagation) {
e.stopPropagation();
}
if (draggedItem != this) { //MH - swap if we're not dragging the item onto itself
var draggedIndex = $('.segmentListItem').index($(draggedItem));
var targetIndex = $('.segmentListItem').index($(this));
if (draggedIndex > targetIndex){
$(draggedItem).insertBefore($(this));
} else {
$(draggedItem).insertAfter($(this));
}
}
return false;
}
CSS:
a { border-radius: 10px; }
a:hover { background: #ccc; }
.segmentListItem { text-align:center; width: 50px; margin-right: 5px; font-size: 16px; display:inline-block; cursor:move; padding:10px; background: #fff; user-select: none; }
.segmentListItem.over { background: #000; color: #fff; }
Status (six years later)
According to https://bugs.webkit.org/show_bug.cgi?id=134555 this used to be a bug. However, it must have been fixed in the meantime, as it cannot be reproduce anymore in modern browsers. The only browser where I could still replicate it was IE11.
Working fix
You can replace the CSS :hover with a .hover class toggled from JS, in order to better control the hover state:
document.querySelectorAll('.segmentListItem a').forEach(function (item) {
item.addEventListener('mouseenter', function () {
this.classList.add('hover');
});
item.addEventListener('mouseleave', function () {
this.classList.remove('hover');
});
});
Code snippet below:
function addDragListeners() {
$(".segmentListItem").each(function(index) {
$(this)[0].addEventListener("dragstart", handleDragStart, false); //rollover for current
$(this)[0].addEventListener("drop", handleDrop, false); //drops dragged element
$(this)[0].addEventListener("dragover", handleDragOver, false); //allows us to drop
$(this)[0].addEventListener("dragenter", handleDragEnter, false); //rollover for target
$(this)[0].addEventListener("dragleave", handleDragLeave, false); //sets dragged item back to normal
$(this)[0].addEventListener("dragend", handleDragEnd, false); //sets all back to normal
});
}
function handleDragEnter(e) {
// this / e.target is the current hover target.
this.classList.add("over");
}
function handleDragLeave(e) {
this.classList.remove("over"); // this / e.target is previous target element.
}
function handleDragEnd(e) {
e.preventDefault();
$(".segmentListItem").removeClass("over"); //removes the over class on all elements
}
function handleDragStart(e) {
draggedItem = this;
e.dataTransfer.effectAllowed = "move";
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
e.dataTransfer.dropEffect = "move"; // See the section on the DataTransfer object.
return false;
}
function handleDrop(e) {
if (e.stopPropagation) e.stopPropagation();
if (draggedItem != this) {
//MH - swap if we're not dragging the item onto itself
var draggedIndex = $(".segmentListItem").index($(draggedItem));
var targetIndex = $(".segmentListItem").index($(this));
if (draggedIndex > targetIndex) {
$(draggedItem).insertBefore($(this));
} else {
$(draggedItem).insertAfter($(this));
}
}
return false;
}
// JS fix starts here:
document.querySelectorAll('.segmentListItem a').forEach(function(item, idx) {
item.addEventListener('mouseenter', function() {
this.classList.add('hover');
});
item.addEventListener('mouseleave', function() {
this.classList.remove('hover');
});
});
// and ends here. Comment these lines, and uncomment the `a:hover` rule in CSS in order to see the initial code
addDragListeners();
a {
border-radius: 10px;
}
/* a:hover {
background: #ccc;
} */
.segmentListItem {
text-align: center;
width: 50px;
margin-right: 5px;
font-size: 16px;
display: inline-block;
cursor: move;
padding: 10px;
background: #fff;
user-select: none;
}
.segmentListItem.over {
background: #000;
color: #fff;
}
.hover {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<ul>
<li class="segmentListItem">
test1
</li>
<li class="segmentListItem">
test2
</li>
<li class="segmentListItem">
test3
</li>
</ul>