61

Saying I have an input type="file" field. One can drop a file on this input(like in Firefox) instead of clicking "browse" and selecting the file.

Now, I want to customize it a bit, by changing the field's background color when one is about to drop a file in the input. I cannot really use :hover since it matches even when you're not drag&dropping. Is there a CSS (pseudo-class) to do that?

And is there a CSS way to style different if the file being dropped is not accepted and if it is? Say, if the field accepts only PNG files using accept attributes, I would make the field green if you're about to drop a PNG file on it, and red if that's another type of file.

Is there a CSS way to do these today? Is there a planned way to do so in CSS (like in upcoming specs/in current specs but not implements anywhere)?

2
  • It might work using the :invalid pseudo selector. As for the "drop-hover", no, not with CSS alone...
    – Andrei V
    Commented Mar 17, 2017 at 9:35
  • @Andrei Neat idea to actually use the :invalid at least for now (because even if I did not think of it, I actually want to show that a "normally selected file" is not acceptable too)
    – Xenos
    Commented Mar 17, 2017 at 10:03

4 Answers 4

29

UPDATE: Thanks to @Renato's comment, according to https://github.com/w3c/csswg-drafts/issues/2257, the drop pseudo-class has been dropped now.


There is :drop and :drop() pseudo-class, which is currently in Working Draft status.

The browser support is not good.

For "file being dropped is not accepted" case, :drop(invalid active) is expected to work, in future.

3
  • Wonderful ! CSS4 would bring (as CSS3 did) a lot of great feature :) Thanks, because I didn't see it in the spec when searching
    – Xenos
    Commented Mar 17, 2017 at 10:01
  • "file being dropped is not accepted" sounds like it should be :drop(invalid active) and not just :drop(invalid), but I'm just picking nits.
    – BoltClock
    Commented Mar 17, 2017 at 11:39
  • 39
    The drop pseudo-class has been dropped: github.com/w3c/csswg-drafts/issues/2257
    – Renato
    Commented Apr 8, 2019 at 19:29
21

I had the same question and solved it a little differently than nashcheez. Still using JavaScript, though (I used jQuery here to simplify things):

function drag(ev) {
  ev.dataTransfer.setData("text", "foo");
}

function allowDrop(ev) {
  $(ev.target).attr("drop-active", true);
  ev.preventDefault();
}

function leaveDropZone(ev) {
  $(ev.target).removeAttr("drop-active");
}

function drop(ev) {
  ev.preventDefault();
  $(ev.target).removeAttr("drop-active");
  alert(ev.dataTransfer.getData("text"));
}
#draggableItem {
  height: 50px;
  width: 150px;
  background-color: #eee;
}

#dropZone {
  height: 50px;
  width: 150px;
  background-color: #efe;
}

#dropZone[drop-active=true] {
  box-shadow: inset 0px 0px 0px 2px #00C;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="draggableItem" draggable="true" ondragstart="drag(event);">Drag Me</div>

<div id="dropZone" ondragover="allowDrop(event);" ondragleave="leaveDropZone(event);" ondrop="drop(event);">Drop Here</div>

I've tested this on Safari, Firefox and Chrome, but I haven't tried IE. I'm probably breaking a rule with the custom attribute, but it seems to work while I'm waiting for CSS4.

6
  • 21
    As much as this meme about jQuery is funny, TobyRush answered the question in a very tidy way. While not the best answer, it's still clear of intent. A competent coder will be able to translate into vanilla JS and a novice will be able to make it work (albeit inefficiently) in jquery.
    – Polyducks
    Commented Jul 9, 2018 at 15:26
  • 2
    Great answer Toby : it helped me a lots thanks ! I translated it back to jquery as I use it everyday at work (I mean in the real world), Hope you don't mind ! ;-)
    – PhilMaGeo
    Commented Sep 13, 2018 at 9:53
  • 4
    Its always amusing to hear some rocket scientist coder talk about the "efficiency" of rare or one-off events
    – Danial
    Commented Jul 28, 2020 at 16:46
  • 1
    Well @John - believe it or not but jQuery helped push the web forward. It provided consistent API when browser support for JavaScript was patchy. It allowed countless professionals & dabblers to deliver the functionality across all browsers. To this day if you are not a programmer or have limited time/resources/skills and just want to add some cool functionality to your web page quickly, jQuery will get you out of the rut. Should jQuery be used in the modern apps - no, definitely not. But for anything smaller that doesn't require Angular, React, Vue, Svelte etc. - it is a very good fit.
    – codeepic
    Commented Aug 27, 2021 at 9:34
  • @codeepic You mean when the web was marching towards standards instead of having a Microsoft and a Netscape DOM it created a third DOM and it's not even compatible with itself across versions. It's a terrible fit for everything because the people wasting their time with it could have used normal JavaScript and I wouldn't be forced to add -jquery to literally every JavaScript search. The price of incompetence is extreme.
    – John
    Commented Aug 27, 2021 at 21:32
7

There is no CSS-Solution. My isue was to change Color on drag-over (Chrome does not support, Opera does)

I do it this way:

`<input id="xyz" type="FILE" value=""  
    ondragover ="this.style.backgroundColor='#88FF88';" 
    ondragleave="this.style.backgroundColor='#FFFFFF';" 
/> 
<input type="button" onclick="srFU(this);" value="send File" />`
6

There is absolutely no pure css cross-browser solution currently for changing element's properties when dragging and dropping elements into the browser window.

What you are trying to do here can be achieved by Javascript/jQuery using a hidden container and showing it only when the object is inside the draggable container.

There is this demo I had saved earlier if you would like to have a look into:

var resetTimer;

var reset = function() {
  $('#d1').hide();
};

var f = function(e) {
  var srcElement = e.srcElement ? e.srcElement : e.target;

  if ($.inArray('Files', e.dataTransfer.types) > -1) {
    e.stopPropagation();
    e.preventDefault();

    e.dataTransfer.dropEffect = (srcElement.id == 'd1') ? 'copy' : 'none';

    if (e.type == "dragover") {
      if (resetTimer) {
        clearTimeout(resetTimer);
      }
      $('#d1').show();
      console.info('dropped on <' + srcElement.tagName.toLowerCase() + ' id="' + srcElement.id + '">\n\ne.dataTransfer.types is ' + e.dataTransfer.types + '\n\ne.dataTransfer.files.length is ' + (e.dataTransfer.files ? e.dataTransfer.files.length : 0));

    } else if (e.type == "dragleave") {
      resetTimer = window.setTimeout(reset, 25);
    } else if (e.type == "drop") {
      reset();
      alert('dropped on <' + srcElement.tagName.toLowerCase() + ' id="' + srcElement.id + '">\n\ne.dataTransfer.files.length is ' + (e.dataTransfer.files ? e.dataTransfer.files.length : 0));
    }
  }
};

document.body.addEventListener("dragleave", f, false);
document.body.addEventListener("dragover", f, false);
document.body.addEventListener("drop", f, false);
body {
  border: 1px solid black;
}

#d0,
#d2 {
  border: 1px solid black;
}

#d1 {
  border: 1px solid black;
  display: none;
  background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
  <div id="d0">drag files onto this page</div>
  <div id="d1">-&gt; drop here &lt;-</div>
  <div id="d2">and stuff will happen</div>
</body>

5
  • 2
    Thanks for taking time to make the snippet, but I want a CSS way to do so. I highly prefer forget about a fancy thing for few months/years (until it reaches CSS spec) rather than adding dependencies on the websites and imperative code to maintain/that often has side effects.
    – Xenos
    Commented Mar 17, 2017 at 10:05
  • Well the js code here would have no side-effects, and would be a clean solution to your problem. But its cool if you want to wait for a pure CSS solution.
    – nashcheez
    Commented Mar 17, 2017 at 10:07
  • I always assume that any JS has side-effect: Will bringing jQuery clash with existing functions ? Will bubbling cancellation break other listeners? Will it bring security issues (XSS/injections/etc)? Will it work on all client and not crash the whole JS? Will JS take priority over browser's/user's custom stylings? That's why I prefer CSS (mostly for the latter btw) ;)
    – Xenos
    Commented Mar 17, 2017 at 10:26
  • To each his own! ;) True there are repercussions to unwise usage of Javascript, but thats how you start writing better javascript. When you know the problems, its easier to find the best solution and not repeat again. :)
    – nashcheez
    Commented Mar 17, 2017 at 10:40
  • 1
    @John If you would read the answer, its clearly mentioned that there is no CSS-specific way to do this. And then there is an alternative provided if the OP would want to utilize (which the OP chose not to). Down-voting an alternative when no current solution exists just showcases your ignorance and is a hindrance to the way the world wants to learn things.
    – nashcheez
    Commented Feb 8, 2018 at 9:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.