HTML5 Drag & Drop and touching text - javascript

I have a weird issue with drag & drop in HTML5.
There are 3 target zones which are divs with text in them and 1 source zone which is a div containing a image.
I use dragenter and dragleave events to change the border of the active target zone to project where the dragged object is going to land.
The problem is that as soon as you drag it over text, it for some reason fires the dragleave event, removing the border.
Here is a jsfiddle example illustrating the problem
And here is some inline code:
HTML
<h1>Targets</h1>
<div class="targets">
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
</div>
<h1>Source</h1>
<div class="source" draggable="true">
<img class="source_image" src="http://lorempixel.com/output/technics-q-c-184-69-8.jpg" alt="image" width="184" height="69"/>
</div>
JS
$("div.source").on('dragstart', function(e) {
$(this).fadeTo('slow', 0.4);
});
$("div.source").on('dragend', function(e) {
$(this).fadeTo('slow', 1);
});
$("div.target").on('dragover', function(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
return false;
});
$("div.target").on('dragenter', function(e) {
$(this).addClass('over');
});
$("div.target").on('dragleave', function(e) {
$(this).removeClass('over');
});
CSS
h1 {
font-size: 2em;
text-align: center;
}
.target {
margin: 1em;
display:inline-block;
width: 184px;
height: 69px;
border: 5px #995555 solid;
border-radius: 5px;
background-color: #AAAAAA;
vertical-align: bottom;
text-align: center;
font-size: 1.1em;
font-weight: bold;
line-height: 0.9em;
}
.target.over {
border: 5px #0A0 dashed;
}
.source {
margin: 1em;
display:inline-block;
width: 184px;
height: 69px;
border: 5px #555599 solid;
border-radius: 5px;
background-color: #CCCCCC;
vertical-align: bottom;
text-align: center;
font-size: 1.4em;
font-weight: bold;
}
Does anyone know of any solutions to keep the border changed even when touching text?
Another question would be whether it is possible to keep the border around the source div being dragged?
On a final note, I realize that both of these things can be done by using jQuery UI draggable and droppable, but I'm specifically wondering if it is possible to do this with native HTML5 drag & drop.

The problem is that the text constitutes an extra node in the DOM, and dragleave and dragenter take into account child elements as well as parent elements. When the cursor enters the text node, it leaves the div. This is similar to the mouseout vs mouseleave issue. A simple way to work around it is to keep a count of the events and only remove the style when all are accounted for:
var count=0;
$("div.target").on('dragenter', function(e) {
$(this).addClass('over');
count++;
}).on('dragleave', function(e) {
if (--count<=0) {
$(this).removeClass('over');
}
});
This isn't necessarily completely reliable (remember to set count to zero in the drop event), but it'll work better than your current setup. Another option is to not put any content in the div elements at all, instead add it with CSS:
.target:after {
content: 'I am a target \A Touch text while dragging to see the problem';
white-space: pre-wrap;
}
This has some accessibility drawbacks, because generated content is invisible to assistive technology, and will only let you add text, but is in many ways a cleaner solution.

Related

Why does the mouseout handler behave so illogically in this case?

Red square is the part of a container with class "parent". If I hover mouse over that red square it disappears. But why? I expected that it shouldn't.
Expected behaviour: it does not disappear since red square is a part of ".parent" container and I have clearly stated, that the mouseout event occurs on that container.
There was a suggestion, that this question is a duplicate of
JavaScript mouseover/mouseout issue with child element
In some way - yes, but I think that this question provides value, because it not only provides the solution ("you can try this"), but also explains WHY you should use that and WHY the initial solution is not working as it is supposed to.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append("<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>");
$(this).on("mouseout", removeSquare);
}
$(".parent").on("mouseover", addSquare);
</script>
It's normal behaviour of .mouseout() event.
Show the number of times mouseout and mouseleave events are triggered.
mouseout fires when the pointer moves out of the child element as
well, while mouseleave fires only when the pointer moves out of the
bound element.
You should use .mouseenter() and .mouseleave() events,
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
$(".parent").on("mouseleave", removeSquare);
.parent {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="parent">Hover mouse over this text<br></span>
As other people have noted, your original problem is that mouseover and mouseout events also fire for child elements. The solution to that issue is either to use jQuery's mouseenter and mouseleave events, or simply to replace the JS code with the CSS :hover pseudo-class.
However, the reason why the other JS and CSS solutions posted here sometimes behave erratically (causing the square to disappear if you move the mouse over it slowly, but not if you move it fast, and not on all browsers even if you move it slowly) is because, depending on your browser and font settings, there may or may not be a small gap between the top line of text and the square below it. If the gap exists, and your mouse cursor hits it while moving from the text to the square, the browser will consider the mouse to have left the parent element, and will thus hide the square.
Setting a (light blue) background color on the parent element shows the issue clearly; depending on what font and line height the browser chooses, the parent element and the box can look like this:
or like this:
Manually setting a particularly large line height makes the problem easily reproducible (CSS example based on Thomas van Broekhoven's answer):
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
There are two general ways to fix this issue. The simplest option, where practical, is to make the parent element a block, thereby eliminating the gaps between the lines. You may also wish to add position: absolute to the square's style, so that it won't expand its parent element when it appears:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
background-color: red;
width: 50px; height: 50px;
}
.parent {
display: block;
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
Alternatively, if you really want to stick with an inline parent element (e.g. because you want it to be able to wrap across several lines of text), you can set a negative top margin on the square to make sure it overlaps the line of text above it. If you don't want the square to visibly overlap the text, you can further move all the visible content of the square into an inner element and set a corresponding positive top margin on it, like this:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
margin-top: -1em;
border: 1px dashed gray; /* to show the extent of this otherwise invisible element */
}
.kvadrat > .inner {
display: block;
margin-top: 1em;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'><span class='inner'></span></span></span>
I know this is not directly answering your JavaScript question, but I would like to open your eyes if you're not bounded to JavaScript. You can easily achieve this with CSS.
.kvadrat {
display: none:
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px;height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='kvadrat'></span></span>
You can achieve the same using CSS.
.child {
display: none:
}
.parent:hover > .child {
display: inline-block;
background-color: red;
width: 50px;
height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='child'></span>
</span>
It is because of event bubbling. When you enter the child span, you jQuery will fire mouseout because you've now gone to a child span. If you want to keep it going, use mouseenter and louseleave which does not fire until you leave the actual element, regardless of child elements.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
$(this).on("mouseleave", removeSquare);
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
</script>

Jquery click on popup div to show another div

The requirement is user can Click on black box to show orange box, and click on orange box to show red box, but the orange box and red box should be hidden
when user click anywhere of the document except the orange box or the
red box itself.
But currently the issue is that we cannot click on orange box to show red box
Would much appreciate if you could help me out, thanks a lot
Demo link: http://plnkr.co/edit/OqlfbmFPKdXx0wDhnLxZ?p=preview
$(function() {
$('#mypop').click(function(e) {
event.stopPropagation();
});
$(document).on('click', '#myclick', function() {
$('#mypop').toggle();
$(document).one('click', function() {
$('#mypop').hide();
});
});
$(document).on('click', '#myclick1', function() {
$('#mypop2').show();
});
$(document).on('click', '#myclick2', function() {
$('#mypop2').show();
});
})()
#mypop {
background-color: orange;
position: absolute;
top: 130px;
left: 50px;
width: 150px;
padding: 15px;
}
.mydiv {
background-color: black;
padding: 30px;
width: 50px;
cursor: pointer;
color: white;
}
#mypop2 {
margin-top: 150px;
background-color: red;
width: 100px;
height: 50px;
padding: 18px;
display: none;
}
#myclick1,
#myclick2 {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myclick" class='mydiv black-box'>
click me!
</div>
<div id="mypop" style="display:none;" class='orange-box'>
<p>hello world</p>
<div id='myclick1'>BUTTON1</div>
<div id='myclick2'>BUTTON2</div>
</div>
<div id="mypop2" class='red-box'>
Hello World!!!
</div>
try this. I think this is what you are excepting but I'm not sure since you keep editing your question.
Demo Link: http://plnkr.co/edit/n7rdgqTwiFrXtpgoX4TQ?p=preview
$('#myclick1').click(function(){
$('#mypop2').show();
});
$('#myclick2').click(function(){
$('#mypop2').show();
});
You have couple of things mixed up.
The main stop-point was the very first event listener
$('#mypop').click(function(e) {
which is incompatible with the rest of listeners
$(document).on('click','#myclick1',function(e){
after I have changed it to
$(document).on('click','#mypop', function(e){
the other listeners have started working.
Second thing is that for embedded elements (parent-child) you need to stop event propagation, otherwise the parent event is triggered as well (which is not desired)
$(document).on('click','#myclick1',function(e){
e.stopPropagation();
:
});
I have also changed the CSS a bit and added class hide to use instead of styles. Toggling this class is what hides and shows an element.

Make popup have smart positioning

I am working on a piece of legacy code for a table. In certain cells, I'm adding a notice icon. When you hover over the icon a <span> is made visible displaying some information. I would like to be able to make this <span> smart about its positioning but can't figure out a good method. I can statically position it but depending on which cell in the table it is in it gets lost against the edge of the page. I have done a JsFiddle here demonstrating the issue. Unfortunately, I am not allowed to use anything but HTML, CSS and vanilla JS.
The title attribute to most tags is pretty smart about its position. I have added a title to one of the cells in the table in the jsFiddle (cell containing "Hello"). Is there any way to make my span exhibit the same smart behaviour?
A pop-up can be added before any element by putting the popup html code inside a 'div' with 'position:absolute; overflow:visible; width:0; height:0'.
When these events: 'onmouseenter', 'onmouseleave' are fired on the element, just toggle the popup css attribute 'display' between 'none' and 'block' of the element.
Example on jsfiddle:
https://jsfiddle.net/johnlowvale/mfLhw266/
HTML and JS:
<div class="popup-holder">
<div class="popup" id="popup-box">Some content</div>
</div>
Some link
<script>
function show_popup() {
var e = $("#popup-box");
e.css("display", "block");
}
function hide_popup() {
var e = $("#popup-box");
e.css("display", "none");
}
</script>
CSS:
.popup-holder {
position: absolute;
overflow: visible;
width: 0;
height: 0;
}
.popup {
background-color: white;
border: 1px solid black;
padding: 10px;
border-radius: 10px;
position: relative;
top: 20px;
width: 300px;
display: none;
}

textarea where part of the text can be moved but NOT deleted

Above is generally what I'm trying to create. There has to be a piece of text which is always in the textarea. It can be dragged to a different part of the text area, but it can never be deleted. You can type both before it and after it.
I've been trying to find the best approach to accomplish this for several days and have come up short. Anyone ideas?
A start , using contentEditable parent element , div having input type="text" element with disabled attribute set to "true" . TODO: Drag and Drop adjustments ; currently only utilizes .appendChild to drop div containing input to parent div , without placing draggable element between editable text in parent contentEditable div
input {
color: dodgerblue;
font-size: 28px;
font-weight: bold;
font-family: arial;
background: none;
border: none;
width: 220px;
padding: 0px;
margin: 0px;
position: relative;
display: inline-block;
}
div[droppable="true"] {
width: 450px;
height: 300px;
border: 1px solid #000;
}
div[draggable="true"] {
width: 250px;
}
div[draggable="true"]:hover {
cursor: pointer;
}
<script>
function dragstartHandler(e) {
// add `input` element's name to the data transfer object
e.dataTransfer.setData("text/plain", e.target.firstElementChild.name);
}
function dropHandler(e) {
e.preventDefault();
// get the `name` of the target and add the moved element to the target's DOM
var data = e.dataTransfer.getData("Text");
e.target.appendChild(
document.querySelector("input[name=" + data + "]").parentElement
);
}
document.ondragover = function(event) {
// prevent default to allow drop
// TODO: drop `input` parent `div` element between `contentEditable` text
event.preventDefault();
console.log(event)
};
</script>
<div contentEditable="true" droppable="true" ondrop="dropHandler(event);">
This text can be edited
<div draggable="true" ondragstart="dragstartHandler(event);">
<input disabled name="fixed" type="text"
minlength="14" maxlength="14" value="BUT THIS CAN'T" />
</div>and so can this one here
</div>

How to enable javascript mouse events on overlapping html elements?

This probably cannot be done, but I have a fixed-position div on top of inline html in the page body. The inline html has clickable elements, and the fixed div has a hover event.
The fixed element is an empty div, so it is invisible.
Currently, the fixed element is blocking click events on the item under it.
Is it possible?
This solution is too complicated
https://stackoverflow.com/a/9616491/209942
Possible solution?
https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
Thx
The fixed element should not be prevent the clicks from the item under it unless you are stopping the event propagation.
See this fiddle: https://jsfiddle.net/pv0mygz5/
-- it demonstrates that without event.stopPropagation the event should be intercepted by the listener on the span element.
$('#click-me').on('click', function (e) {
console.log('click triggered');
});
$('.box').on('mouseover', function (e) {
//don't stop event from bubbling
console.log('hover triggered');
});
Could you also include a code snippet that demonstrates your problem?
although IE10 doesn't support it you can use
pointer-events: none;
http://jsfiddle.net/leaverou/XxkSC/light/
In this fiddle you can see a drop down being covered with other elements, the other elements has pointer-events: none so you can click on the arrow down button and the click actually goes to the select element itself.
BR,
Saar
You can also try using z-index. Depending on your layout it may not be a solution, but if your front div is invisible, then it shouldn't create unwanted effect. Like this for example:
document.querySelector('#under').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
document.querySelector('#notunder').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
#fix {
width: 60px;
height: 200px;
position: fixed;
z-index: -1;
top: 0px;
border: 1px solid black;
}
#under {
display: inline;
}
#fixnozindex {
width: 100px;
height: 200px;
position: fixed;
left: 75px;
top: 0px;
border: 1px solid black;
}
#notunder {
display: inline;
}
<div id="fix"></div>
<div id="under">Clickable</div>
<div id="fixnozindex"></div>
<div id="notunder">Not clickable</div>

Categories

Resources