I am trying to create a simple drag and drop activity in Adobe CC.
I have got the dragging function working fine, however I can't get it to detect when the dragged button hits the target.
Any help would be greatly appreciated!
Here is my code:
function Main()
{
createjs.Touch.enable(stage);//Emable Touch Gesture Recognition
createjs.Ticker.addEventListener("tick", this.update.bind(this));
this.addTargetsToButtons();//Add Button Actions
}
var phonemes = ["s","a","t","p","i","n","m","d","g","o","c","k","ck","e","u","r","h","b","f","ff","l","ll","ss"];
var draggablePhonemes = [this.b0, this.b1, this.b2];
var targets = [this.target1];
Main.prototype.addTargetsToButtons= function(){//Add Actions To All Our Buttons
for (i = 0; i<draggablePhonemes.length; i++){
console.log(draggablePhonemes[i]);
draggablePhonemes[i].addEventListener("pressmove", draggedStart.bind(this));
draggablePhonemes[i].addEventListener("pressup",draggedStop.bind(this));
draggablePhonemes[i].name = phonemes[i];
}
}
function draggedStart(event) {
console.log(event.currentTarget.name)
var p = stage.globalToLocal(event.stageX, event.stageY);
event.currentTarget.x = p.x;
event.currentTarget.y = p.y;
//The buttons can be dragged onto target1, target2, target3
}
function draggedStop(event) {
console.log(event.currentTarget.name + " Has Stopped Moving")
if (event.currentTarget.hitTest(targets[0].x,targets[0].y)){
console.log("Collision");
}
}
You need to add ondragover data attribute to the target and set it to this function:
function allowDrop(event) {
event.preventDefault();
console.log("Look, it hits the target");
}
By default, data/elements cannot be dropped in other elements. To allow that to happen, you must prevent the default handling of the element.
Note: You might also need to add ondrop data attribute to the target set to a function, so the actual drop can happen.
ex.
function drop(event) {
event.preventDefault();
var data = event.currentTarget.name;
event.target.appendChild(document.getElementById(data));
}
And this is what the html element should look like:
<div ondrop="drop(event)" ondragover="allowDrop(event)"></div>
Related
When modifying a feature, there is the option to delete a single vertex. According to the docs it says:
removePoint(){boolean}:
Removes the vertex currently being pointed.
(https://openlayers.org/en/latest/apidoc/ol.interaction.Modify.html)
Since I work on mobile devices, I would like to present a popup with a delete button next to the a vertex if a user clicks or hovers over it. Therefore I need the coordinates of this vertex. I can see the vertex indicated on the map with a different style, but how can I get it or its coordinates?
It must be somewhere in some selection because the automatic "pointing to" style and removePoint method works per se fine.
Here is a solution that use a button to delete a vertex.
The button is shown or hidden if there is a vertex to delete (it could be a popup).
It uses:
condition option to show the button if there is a point near the click
insertVertexCondition option to hide the button (there is no vertex here)
modifystart and modifyend event to hide the button (we are moving and we don't want the button to be visible)
removePoint function when click on the button
The button is not show if you're moving or if there is no point to delete.
It doesn't rely on features that are not documented or private.
You can see it in action here.
var btElt = document.getElementById("delete");
// Modify interaction
var mod = new ol.interaction.Modify({
source: vector.getSource(),
// Show on select
condition: function(e){
// Check if there is a feature to select
var f = this.getMap().getFeaturesAtPixel(e.pixel,{
hitTolerance:5
});
if (f) {
var p0 = e.pixel;
var p1 = f[0].getGeometry().getClosestPoint(e.coordinate);
p1 = this.getMap().getPixelFromCoordinate(p1);
var dx = p0[0]-p1[0];
var dy = p0[1]-p1[1];
if (Math.sqrt(dx*dx+dy*dy) > 8) {
f = null;
}
}
if (f) btElt.style.display = "inline-block";
else btElt.style.display = "none";
return true;
},
// Hide on insert
insertVertexCondition: function(e) {
btElt.style.display = "none";
return true;
}
});
// Hide on modifying
mod.on(['modifystart','modifyend'], function(){
btElt.style.display = "none";
});
map.addInteraction (mod);
// Delete vertex when click the button
function deleteVertex() {
mod.removePoint();
btElt.style.display = "none";
}
Alright digging into the source code I came up with a kinda ugly but so far working solution. For the record, this is the code.
this.modify = new Modify({
features: new Collection([this.selectedFeature]),
pixelTolerance:30,
condition: (event)=>{
if(this.modify["lastPointerEvent_"] && this.modify["vertexFeature_"])
this.removePointPopup.setPosition(this.modify["lastPointerEvent_"].coordinate);
else
this.removePointPopup.setPosition(undefined);
return true;
}
});
this.modify.on("modifyend",ev=>{
this.removePointPopup.setPosition(ev.target["lastPointerEvent_"].coordinate);
})
[...]
removePoint(){
this.modify.removePoint()
this.removePointPopup.setPosition(undefined);
}
If someone knows about a nicer solution, feel free to post it.
I'm creating an additional tab on a menu dynamically (let's call this new tab, Watch), and all was going pretty well including the submenu that showed up once the new tab was hovered over. I looked at articles on event bubbling and took a look at other examples, but couldn't find a solution to my issue.
Whenever I hover over Watch, the submenu appears, but when I try to hover from Watch to its submenu, the submenu disappears. I need the submenu to persist when a user hovers over Watch or the submenu, and the submenu should only disappear once the user hovers out of either. Just to note, I cannot use CSS for my solution. I've attached my current code below with comments:
//PREPEND AS FIRST CHILD
var prependChild = function(parent, newFirstChild) {
parent.insertBefore(newFirstChild, parent.firstChild)
}
//DECLARING VARS
var navMenu = document.getElementsByClassName('navGlobal-list')[0];
categoryExplorer = document.getElementsByClassName('categoryExplorer')[0];
//CREATING NEW TAB
var exploreTab = document.createElement('li');
exploreTab.className = 'navGlobal-category';
//CREATING NEW SEARCH FORM
var searchHtml = ['<div class="searchProgram searchProgram--categoryExplorer">',
'<div class="searchProgram-container">',
'<input type="search" class="form-control form-control--light form-control--searchProgram" placeholder="Search programs" value="">',
'</div>',
'</div>'].join('');
//CREATING NEW WATCH CATEGORY EXPLORER CONTENT
var watchCategoryExplorerContent = document.createElement('div');
watchCategoryExplorerContent.className = 'categoryExplorer-content target-watch-content';
watchCategoryExplorerContent.innerHTML = searchHtml;
prependChild(categoryExplorer, watchCategoryExplorerContent)
var watchLink = document.createElement('a');
watchLink.setAttribute('href','/watch');
watchLink.innerHTML = 'watch'.toUpperCase();
exploreTab.appendChild(watchLink);
navMenu.appendChild(exploreTab); //ADDED 'WATCH' TO THE NAVIGATION
//CHANGE CLASSES ON HOVER
exploreTab.addEventListener("mouseover", function() {
exploreTab.className = 'navGlobal-category navGlobal-category--open';
categoryExplorer.className = 'categoryExplorer categoryExplorer--open';
watchCategoryExplorerContent.className = 'categoryExplorer-content categoryExplorer-content--open target-watch-content';
}, false);
exploreTab.addEventListener("mouseleave", function() {
exploreTab.className = 'navGlobal-category';
categoryExplorer.className = 'categoryExplorer';
watchCategoryExplorerContent.className = 'categoryExplorer-content target-watch-content';
}, false);
A potential (layout-dependent) way to keep the menu open would be to make it a child of the tab - that way, provided there is no space between the tab and the hover menu, you can hover from one to the other without creating a mouseleave event on the tab.
Another solution that is not layout-dependent would be to add some delay between the initial mouseleave event and the submenu closing. I've done something like this using jQuery before, but the same thing should be possible without it.
$('.navGlobal-category').mouseleave(function(){
setTimeout(
function(){
if(!isHovered($('.navGlobal-category')[0])){
exploreTab.className = 'navGlobal-category';
categoryExplorer.className = 'categoryExplorer';
watchCategoryExplorerContent.className = 'categoryExplorer-content target-watch-content';
}
}, 200);
});
function isHovered(e){
return ((e.parentNode.querySelector(":hover") ||
e.querySelector(":hover")) === e);
}
Credit to zb' for the isHovered solution: https://stackoverflow.com/a/14800287/5403341
For the non-layout solution #B1SeeMore suggests you don't actually need a delay.
Here is a working demo: https://jsfiddle.net/jw22ddzk/
<div id="one" class="menuitem"></div>
<div id="two" class="menuitem" style="display: none;"></div>
var elems = document.getElementsByClassName("menuitem");
for (var i = 0; i < elems.length; i += 1) {
elems[i].addEventListener("mouseleave", function () {
console.log("leave", this.id);
document.getElementById("two").style.display = "none";
});
elems[i].addEventListener("mouseenter", function () {
console.log("enter", this.id);
document.getElementById("two").style.display = "";
});
}
The trick is that mouseenter fires for two even if mouseleave hides the element. Just remember to show the element again. A likey explanation is that the mouseenter and mouseleave events spawn in pairs. So mouseenter happens regardless of the effects of mouseleave.
Note that this only works if the elements are beside eachother with pixel accuracy.
Another note: I notice that you're using mouseover and mouseleave. I wouldn't recommend doing that. There are two event pairs for detecting hovers: mouseenter/leave and mouseover/out. They are different events. They differ specifically in that mouseover/out will trigger also for child elments. My recommendation is that you don't interchange the pairs or you might get unexpected behaviour.
Alright, I have a chess board with all the spots and pieces. It's generated onload with two four cycles from a 2D array. The idea is, that you should be able to drag a chess piece (something I've already implemented correctly), and let it go in a valid position. The letting go part should trigger an event in the actual board spots. So each of the 64 spots have an even listener that looks like this.
element.on("mouseup",function(e){
if(Game.current==undefined||!$(this).hasClass("valid")){return;}
var col,row;
col = $(this).attr("column");
row = $(this).attr("row");
Game.current.attr({
"column":col,
"row":row
});
Game.move(Game.current);
Game.changeTurn();
Game.current = undefined;
});
The function triggers normally when I click on a square in the board, however when I drag and drop a chess piece there, it doesn't register that the mouse is up. It let's go of the piece, however since the cursor is directly over the chess piece and said piece "blocks" the cursor from being directly above the spot. It is also worth noting that my HTML structure is like this.
<board>
<spot></spot>
<piece></piece>
</board>
By which I mean, that the pawns, bishop etc. aren't inside the spots, so the event doesn't have anything to "bubble" through.
A better approach would be attach the listener of mouseup to the window object doing something like this
$(window).on('mouseup', on_mouse_up );
and then, from the mouse position calculating the right spot.
function on_mouse_up( e ) {
// Get the mouse position
x = e.pageX;
y = e.pageY;
// Calculate the spot from the mouse position
}
(function ($) {
"use strict";
//console.log($);
$('document').ready(function () {
var target;
$('#wrapper').mousedown(function (e) {
var el = $(e.target);
if (el.hasClass('elem')) {
target = el;
}
});
$('#wrapper').mouseup(function (e) {
var el = $(e.target);
//console.log(e);
if (target) {
if (el.hasClass('kletca')) {
el.append(target);
}
}
target = undefined;
});
});
}(jQuery));
http://jsfiddle.net/32DTv/ a simple example
I am working on some jQuery/JavaScript that makes it possible to drag a div around and simultaneously be able to manipulate other divs (specifically images) on the page. The movable div is basically a transparent rectangle that is meant to simulate a lens. The problem I am having is that I cannot figure out how to pass clicks through to the images below the movable div. I have read up on the pointer-events CSS property and tried setting that to none for the movable div, but that makes the movable div no longer movable. Is there a way for me to pass clicks through this movable div while keeping it movable?
EDIT: To all those asking for my current code, here is the JavaScript that I have so far:
<script>
$(document).ready(function(){
$('img').click(function(e) {
$(document).unbind('keypress');
$(document).keypress(function(event) {
if ( event.which == 115) {
$(e.target).css('width', '+=25').css('height', '+=25');
};
if ( event.which == 97) {
$(e.target).css('width', '-=25').css('height', '-=25');
};
});
});
//code to drag the lens around with the mouse
$("#draggableLens").mousemove(function(e){
var lensPositionX = e.pageX - 75;
var lensPositionY = e.pageY - 75;
$('.lens').css({top: lensPositionY, left: lensPositionX});
});
});
</script>
I created a demo that is proof of concept using document.elementFromPoint to locate the nearest image the moveable element is over. I used jQueryUI draggable to simplify event handling.
The trick with using document.elementFromPoint is you must hide the element you are dragging just long enough to look for other elements, or the draggging element is itself the closest element.
Adding an active class to the closest element allows clicking on the viewer to access the active element
Demo code uses LI tags instead of IMG
var $images = $('#list li');
timer = false;
$('#viewer').draggable({
drag: function(event, ui) {
if (!timer) {
timer = true;
var $self = $(this);
/* use a timeout to throttle checking for the closest*/
setTimeout(function() {
/* must hide the viewer so it isn't returned as "elementFromPoint"*/
$self.hide()
var el = $(document.elementFromPoint(event.pageX, event.pageY));
$('.active').removeClass('active');
if ($el.is('li')) {
$el.addClass('active')
}
$self.show()
timer = false;
}, 100);
}
}
}).click(function() {
if ($('.active').length) {
msg = 'Clicked on: ' + $('.active').text();
} else {
msg = 'Click - No active image';
}
$('#log').html(msg + '<br>');
})
DEMO: http://jsfiddle.net/nfjjV/4/
document.elementFromPoint is not be supported in older browsers. You could also use jQuery position or offset methods to compare coordinates of elements with the current position of the viewer for full browser compatibility
I'm trying to drag and drop items on a page, but insteads of moving the item itself, I create a copy of it.
Here is my code. "copyDragDrop" is a div at the bottom of the page. It remains empty until the user strats dragging something.
function coordSouris()
{
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function drag()
{
var pos = coordSouris(event);
copie = event.srcElement.cloneNode(true);
document.getElementById('copieDragDrop').appendChild(copie);
copie.style.position = 'absolute';
copie.style.display = 'block';
document.onmousemove = mouseMove;
document.onmouseup = drop;
}
function mouseMove()
{
if (copie != null)
{
var pos = coordSouris(event);
copie.style.left = pos.x;
copie.style.top = pos.y;
}
}
function drop()
{
var divCopie = document.getElementById('copieDragDrop');
if (divCopie.hasChildNodes() )
{
while ( divCopie.childNodes.length >= 1 )
{
divCopie.removeChild(divCopie.firstChild);
}
}
}
This code creates the copy, starts to move it, but after a few pixels, the copy stops following the mouse. If I release the mouse, "onmouseup" is not fired, but the copy starts to follow the mouse again ! I've tried the code on several items, the same bug occurs
I don't understand anything, any help is more than welcome.
UPDATE : I juste realised that all elements I tried the code on had something in common : they contained or were included in an ASP.net hyperlink control. The same code works well on regular HTML elements. There must be some auto-generated javascript for links that interferes with my code.
Couldn't find the auto-generated code responsible for the issue, so I simply solvec this by replacing Hyperlink controls by standard HTML anchors.