JochenJung
JochenJung

Reputation: 7213

How to detect wether the mouse is over the :before or over the :after part of an element

I have this CSS to define drop areas, where a user can either drop a section before or after existing sections.

.section:before,
.section:after {
    content: "[insert here]";
    height: 64px;
    line-height: 56px;
    width: 100%;
    display: block;
    border: 3px dashed #aaa;
}

Using JavaScript + JQuery here is the drop listener, that detects the element currently under the mouse:

elem.on('drop', function(e) {
  e.preventDefault();

  var container = $(elem[0].elementFromPoint(e.clientX, e.clientY));
});

However container would be the same element for both the :before and :after case.

How will I be able to know if the user has dropped before or after the section?

Upvotes: 5

Views: 128

Answers (4)

Danield
Danield

Reputation: 125521

This is actually possible.

(Although - as others have mentioned - probably not the the best idea)

Assuming that the content is stacked one above the other with: before -> content -> after

We can calculate the dropping point with respect to the container and then - based on the height of the generated content we can determine if the element was dropped in the before or after zone.

And yes, javascript can actually access css properties of pseudo-elements.

This is done with Window.getComputedStyle()

Syntax: (from MDN)

var style = window.getComputedStyle(element[, pseudoElt]);

pseudoElt Optional

A string specifying the pseudo-element to match. Must be omitted (or null) for regular elements.

So, for example, to get the height of the generated content before the section (let's call it 'target'):

window.getComputedStyle(target, ':before').height 

Here's a Demo snippet:

var elem = document.getElementById("el");
var target = document.getElementById("target");
  
var targetHeight = parseFloat(window.getComputedStyle(target).height);
var beforeHeight = parseFloat(window.getComputedStyle(target, ':before').height);
var afterHeight = parseFloat(window.getComputedStyle(target, ':after').height);

elem.addEventListener("drag", function(e) {
  document.body.classList.remove('dropped');
});

target.addEventListener("dragover", function(e) {
  this.textContent = "dragging over section";
  document.body.classList.add('dragging-over');
  addBeforeAfterClasses(e);
});

target.addEventListener("dragleave", function(e) {
  document.body.classList.remove('dragging-over');
  this.textContent = "section";
  e.currentTarget.style.background = "none";
});

target.addEventListener("drop", function(e) {
  document.body.classList.add('dropped');
  addBeforeAfterClasses(e);
  this.textContent = "successfully dropped!";
});

function addBeforeAfterClasses(e) {
  var dropOffsetTopWithRespectToContainer = e.clientY - target.offsetTop;
  if(dropOffsetTopWithRespectToContainer <= beforeHeight) {
    document.body.classList.add('before');
  } else {
    document.body.classList.remove('before');
  }
  if(dropOffsetTopWithRespectToContainer > targetHeight - beforeHeight) {
    document.body.classList.add('after');
  } else {
    document.body.classList.remove('after');
  }
}

target.ondrop = drop_handler;
target.ondragover = dragover_handler;

function drop_handler(e) {
  e.preventDefault();
}

function dragover_handler(e) {
  e.preventDefault();
}
.section {
  margin: 10px;
  position: relative;
}
.section:before,
.section:after {
    content: "[insert here]";
    height: 64px;
    line-height: 56px;
    width: 100%;
    display: block;
    border: 3px dashed #aaa;
}

.dragging-over.before .section:before {
  content: "[drop into before]";
  border-color: green;
}

.dragging-over.after .section:after {
  content: "[drop into after]";
  border-color: green;
}

.dropped.before .section:before {
  content: "[dropped into before]";
  background-color: green;
  color: white;
}

.dropped.after .section:after {
  content: "[dropped into after]";
  background-color: green;
  color: white;
}

.elem {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: maroon;
  margin: 0 20px;
  display: inline-block;
}
<div id="target" class="section">section</div>
<span>drag me:</span>
<div id="el" draggable="true" class="elem"></div>

Codepen demo

Upvotes: 1

Gaurang Dave
Gaurang Dave

Reputation: 4046

I think you can not manipulate the pseudo elements ::before and ::after, as technically there are not DOM and therefore is inaccessible by any JavaScript.

Upvotes: 2

kLabz
kLabz

Reputation: 1847

::before and ::after are css pseudo-elements which javascript do not know about. For javascript, they are both their parent element.

You might want to use real html elements to do that.

Upvotes: 3

Vinod Bhavnani
Vinod Bhavnani

Reputation: 2225

:before and :after are not part of the DOM, hence they are not accessible to any Javascript.

However the only solution I can think of is to calculate the height of the section and then compare it with the drop x and y coordinates to determine whether it was dropped before or after.

Upvotes: 2

Related Questions