tooltip in pure JS - javascript

I am trying this code but i get: document.getElementsByName(...).style is undefined
I have also a problem with the delegation, i think. Any help?
<html>
<head>
<style type="text/css">
#toolTip {
position:relative;
width:200px;
margin-top:-90px;
}
#toolTip p {
padding:10px;
background-color:#f9f9f9;
border:solid 1px #a0c7ff;
-moz-border-radius:5px;-ie-border-radius:5px;-webkit-border-radius:5px;-o-border-radius:5px;border-radius:5px;
}
#tailShadow {
position:absolute;
bottom:-8px;
left:28px;
width:0;height:0;
border:solid 2px #fff;
box-shadow:0 0 10px 1px #555;
}
#tail1 {
position:absolute;
bottom:-20px;
left:20px;
width:0;height:0;
border-color:#a0c7ff transparent transparent transparent;
border-width:10px;
border-style:solid;
}
#tail2 {
position:absolute;
bottom:-18px;
left:20px;
width:0;height:0;
border-color:#f9f9f9 transparent transparent transparent;
border-width:10px;
border-style:solid;
}
</style>
<script type='text/javascript'>
function load () {
var elements = document.getElementsByName('toolTip');
for(var i=0; i<elements.length; i++) {
document.getElementsByName(elements[i]).style.visibility = 'hidden';
}
}
</script>
</head>
<body onload="load()">
<br><br><br><br><br><br><br><br><br><br><br><br>
<a class="hd"
onMouseOver="document.getElementsByName('toolTip')[0].style.visibility = 'visible'"
onmouseout ="document.getElementsByName('toolTip')[0].style.visibility = 'hidden'">aqui</a>
<div id="toolTip" name="toolTip">
<p>i can haz css tooltip</p>
<div id="tailShadow"></div>
<div id="tail1"></div>
<div id="tail2"></div>
</div>
<br><br><br>
<a class="hd"
onMouseOver="document.getElementsByName('toolTip')[0].style.visibility = 'visible'"
onmouseout ="document.getElementsByName('toolTip')[0].style.visibility = 'hidden'">aqui</a>
<div id="toolTip" name="toolTip">
<p>i can haz css tooltip</p>
<div id="tailShadow"></div>
<div id="tail1"></div>
<div id="tail2"></div>
</div>
</body>
</html>
demo

Try changing the id toolTip to a class:
<div class="toolTip">...</div>
And change your JS to use the display style-thing, rather than visibility, nd the onmouseover's are best dealt with using JS event delegation:
function load()
{
var i, tooltips = document.getElementsByClassName('toolTip'),
mouseOver = function(e)
{//handler for mouseover
e = e || window.event;
var i, target = e.target || e.srcElement,
targetToolTip = target.nextElementSibling || nextSibling;//gets the next element in DOM (ie the tooltip)
//check if mouse is over a relevant element:
if (target.tagName.toLowerCase() !== 'a' || !target.className.match(/\bhd\b/))
{//nope? stop here, then
return e;
}
targetToolTip.style.display = 'block';//make visible
for (i=0;i<tooltips.length;i++)
{//closures are neat --> you have a reference to all tooltip elements from load scope
if (tooltips[i] !== targetToolTip)
{//not the one you need to see
tooltips[i].style.display = 'none';
}
}
};
for (i=0;i<tooltips.length;i++)
{
tooltips[i].style.display = 'none';
}
//add listener:
if (document.body.addEventListener)
{//IE > 9, chrome, safari, FF...
document.body.addEventListener('mouseover',mouseOver,false);
}
else
{//IE8
document.body.attachEvent('onmouseover',mouseOver);
}
}
Google JavaScript event delegation and closures if this code isn't clear, but that's just how I would tackle this kind of thing. IMO, it's fairly efficient (you could use the closure scope to keep track of the tooltip that's currently visible and not loop through all of them, too, that would be even better:
function load()
{
var i, tooltips = document.getElementsByClassName('toolTip'),
currentToolTip,//<-- reference currently visible
mouseOver = function(e)
{
e = e || window.event;
var i, target = e.target || e.srcElement,
targetToolTip = target.nextElementSibling || nextSibling;
if (target.tagName.toLowerCase() !== 'a' || !target.className.match(/\bhd\b/) || targetToolTip === currentToolTip)
{//add check for currently visible TT, if so, no further action required
return e;
}
if (currentToolTip !== undefined)
{
currentToolTip.style.display = 'none';//hide currently visible
}
targetToolTip.style.display = 'block';//make new visible
currentToolTip = targetToolTip;//keep reference for next event
};
for (i=0;i<tooltips.length;i++)
{
tooltips[i].style.display = 'none';
}
if (document.body.addEventListener)
{
document.body.addEventListener('mouseover',mouseOver,false);
}
else
{
document.body.attachEvent('onmouseover',mouseOver);
}
}
And you're there.
Edit:
To hide the tooltip on mouseout, you can either add a second listener directly:
function load()
{
var i, tooltips = document.getElementsByClassName('toolTip'),
currentToolTip,//<-- reference currently visible
mouseOver = function(e)
{
e = e || window.event;
var i, target = e.target || e.srcElement,
targetToolTip = target.nextElementSibling || nextSibling;
if (target.tagName.toLowerCase() !== 'a' || !target.className.match(/\bhd\b/) || targetToolTip === currentToolTip)
{//add check for currently visible TT, if so, no further action required
return e;
}
if (currentToolTip !== undefined)
{
currentToolTip.style.display = 'none';//hide currently visible
}
targetToolTip.style.display = 'block';//make new visible
currentToolTip = targetToolTip;//keep reference for next event
},
mouseOut = function(e)
{
e = e || window.event;
var movedTo = document.elementFromPoint(e.clientX,e.clientY);//check where the cursor is NOW
if (movedTo === curentToolTip || currentToolTip === undefined)
{//if cursor moved to tooltip, don't hide it, if nothing is visible, stop
return e;
}
currentTooltip.style.display = 'none';
currentTooltip = undefined;//no currentToolTip anymore
};
for (i=0;i<tooltips.length;i++)
{
tooltips[i].style.display = 'none';
}
if (document.body.addEventListener)
{
document.body.addEventListener('mouseover',mouseOver,false);
document.body.addEventListener('mouseout',mouseOut,false);
}
else
{
document.body.attachEvent('onmouseover',mouseOver);
document.body.attachEvent('onmouseout',mouseOut);
}
}
Note, this is completely untested. I'm not entirely sure if IE < 9 supports elementFromPoint (gets the DOM element that is rendered at certain coordinates), or even if the IE event object has the clientX and clientY properties, but I figure a quick google will tell you more, including how to get the coordinates and the element that is to be found under the cursor in old, crummy, ghastly IE8, but this should help you on your way. Of course, if you don't want the contents of the tooltip to be selectable, just change the mouseOut function to:
mouseOut = function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (currentToolTip)
{
currentToolTip.style.diplay = 'none';
currentToolTip = undefined;
}
};
No need to check if the mouseout was on the correct element, just check if there is a current tooltip, and hide it.

Try using classes to mark the tooltips:
<div id="toolTip1" class="toolTip">
<p>i can haz css tooltip</p>
<div id="tailShadow"></div>
<div id="tail1"></div>
<div id="tail2"></div>
</div>
And JQuery to toggle the visibility using the class as the selector:
$('.toolTip').attr('visibility', 'hidden')
Definitely clean up the non-unique Id's - this will cause you no end of troubles otherwise

Your problem is likely because you're using the same id for both the tooltips. This is invalid; an id should be unique -- only one element in a given page should have a specific ID.
If you need a shared identifier for multiple objects, use a class instead.

I built a tooltip with a border in pure js that doesn't use hover.
html
<div id="infoId" class='info' style="font-variant:small-caps;text-align:center;padding-top:10px;">
<span id="innerspanid">
</span>
</div>
</div>
<input id="startbtn" class="getstartedbtn" type="button" value="Start >" />
</div>
js
function getTextWidth(text, font) {
// re-use canvas object for better performance
const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
const context = canvas.getContext("2d");
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
}
function getCssStyle(element, prop) {
return window.getComputedStyle(element, null).getPropertyValue(prop);
}
function getCanvasFontSize(el = document.body) {
const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
const fontSize = getCssStyle(el, 'font-size') || '16px';
const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
return `${fontWeight} ${fontSize} ${fontFamily}`;
}
let arrowDimensionWidth = 20;
let arrowDimensionHeight = 20;
let tooltipTextHorizontalMargin = 50;
function openTooltip(text) {
let innerSpan = document.getElementById("innerspanid");
innerSpan.innerHTML = text;
let computedW = getTextWidth(text, getCanvasFontSize(innerSpan)) + tooltipTextHorizontalMargin;
let pointer = document.getElementById('pointer')
pointer.style.right = (((computedW / 2) - (arrowDimensionWidth / 2)) - (0)) + 'px';
let elem = document.getElementById('tooltipHost').parentNode.querySelector('div.info_container');
elem.style.left = ((tooltipHost.getBoundingClientRect().width - computedW) / 2) + "px";
elem.style.width = computedW + "px";
elem.style.display = 'block';
}
function buildTooltip() {
let elements = document.querySelectorAll('div.tooltip');
// Create a canvas element where the triangle will be drawn
let canvas = document.createElement('canvas');
canvas.width = arrowDimensionWidth; // arrow width
canvas.height = arrowDimensionHeight; // arrow height
let ctx = canvas.getContext('2d');
ctx.strokeStyle = 'darkred'; // Border color
ctx.fillStyle = 'white'; // background color
ctx.lineWidth = 1;
ctx.translate(-0.5, -0.5); // Move half pixel to make sharp lines
ctx.beginPath();
ctx.moveTo(1, canvas.height); // lower left corner
ctx.lineTo((canvas.width / 2), 1); // upper right corner
ctx.lineTo(canvas.width, canvas.height); // lower right corner
ctx.fill(); // fill the background
ctx.stroke(); // stroke it with border
ctx.fillRect(0, canvas.height - 0.5, canvas.width - 1, canvas.height + 2); //fix bottom row
// Create a div element where the triangle will be set as background
pointer = document.createElement('div');
pointer.id = "pointer"
pointer.style.width = canvas.width + 'px';
pointer.style.height = canvas.height + 'px';
pointer.innerHTML = ' ' // non breaking space
pointer.style.backgroundImage = 'url(' + canvas.toDataURL() + ')';
pointer.style.position = 'absolute';
pointer.style.top = '2px';
pointer.style.zIndex = '1'; // place it over the other elements
let idx;
let len;
for (idx = 0, len = elements.length; idx < len; ++idx) {
let elem = elements[idx];
let text = elem.querySelector('div.info');
let info = document.createElement('div');
text.parentNode.replaceChild(info, text);
info.className = 'info_container';
info.appendChild(pointer.cloneNode());
info.appendChild(text);
}
}
window.addEventListener('load', buildTooltip);
window.addEventListener('load', wireup);
function wireup() {
document.getElementById('startbtn').addEventListener('click', function (evt1) {
openTooltip("bad email no # sign");
return false;
});
}
css
div.tooltip {
position: relative;
display: inline-block;
}
div.tooltip > div.info {
display: none;
}
div.tooltip div.info_container {
position: absolute;
left: 0px;
width: 100px;
height: 70px;
display: none;
}
div.tooltip div.info {
position: absolute;
left: 0px;
text-align: left;
background-color: white;
font-size: 18px;
left: 1px;
right: 1px;
top: 20px;
bottom: 1px;
color: #000;
padding: 5px;
overflow: auto;
border: 1px solid darkred;
border-radius: 5px;
}

Related

addEventListener is not working on js generated elements (images)

I'm placing a bunch of images on a grid in the center of the page and want to add a check for when each individual image is clicked. The images are created with js and added to the document, could it be an issue of them not being 'ready' yet or something?
function placePieces() {
for (var i = 0; i < setup.length; i++) {
if ((setup[i]+'' == "undefined")) {continue;}
var element = document.createElement("img");
element.src = "Images/" + pieces[Object.keys(pieces)[setup[i]]] + ".png";
element.style.width = "10vh";
element.style.height = "10vh";
element.style.marginTop = (Math.floor(i/8) * 10) + "vh";
element.style.marginLeft = "calc(((100vw - 80vh)/2) + " + (10 * (i%8) - 1) + "vh)";
element.style.zIndex = 10;
element.style.position = "absolute";
element.id = i+1;
document.body.innerHTML = "\n" + element.outerHTML + document.body.innerHTML;
console.log(element.outerHTML)
var nelement = document.getElementById(i+1);
console.log(nelement)
nelement.addEventListener("click",highlight);
}
}
placePieces()
function highlight(n) {
console.log(n)
n = n.currentTarget.myParam;
if (setup[n] == 0 || setup[n] == 6) {
var moves = [];
var m = n
while (True) {
if (!(Math.floor((m-9)/8)<=0)) {
console.log("test")
}
}
}
}
The second function is far from finished but it still does not return anything when it should.
You don't have to grab the element again, you can add the listener directly
const element = document.createElement('div')
element.style.background = 'red'
element.style.width = '100px'
element.style.height = '100px'
element.addEventListener('click', () => console.log('click'))
document.body.appendChild(element)
This should work:
function placePieces() {
for (var i = 0; i < setup.length; i++) {
if ((setup[i]+'' == "undefined")) {continue;}
var element = document.createElement("img");
// ...
element.addEventListener("click",highlight);
}
}
Event Handling
You have two choices:
Method
Pros
Cons
A
Bind (or register) the "click" event to each <img>.
Easier to write
Any dynamically added <img> must be registered to "click" event.
B
Bind the "click" event to an ancestor tag in which all <img>'s reside within. Write the event handler so that it only reacts when an <img> is clicked. This paradigm is called event delegation
Only needs to register to the event to only one tag once and any dynamically added <img> do not need any binding
Writing the event handler is harder.
When reviewing the example:
Click some <img> in Area A and B. There should be a blue outline.
Next, click both ADD buttons.
Click some of the new <img>s. The new <img> in Area A do not work.
Click the BIND button.
Click any of the new <img> in Area A
Details are commented in example
// File names of all images
const images = ["Fqsw6v8/2s", "Qb6N0dG/3s", "qnGtC68/4s", "nDFmjJB/5s", "sPtNDGm/6s", "HpmggvF/7s", "dKfcwxQ/8s", "K7HbrWp/9s", "9ys8PXt/as", "HVK2zvw/bs", "7SgXHz2/cs", "StdB11X/ds", "cN9CnV5/es"];
// File names of the first 3 images which will be added to DOM at page load
const init = [images[0], images[1], images[2]];
/**
* Generate one or more <img>s from a given array/
* #param {Array} array - An array of file names
* #param {String|Object} node - Either a selector string or a DOM object referenced to
* be the elemment to append the <img>s to.
* #param {String} css - A className to be assigned to each <img> #default
* is "img"
* #returns {array} - An array of <img>
*/
function genImg(array, node, css = "img") {
let root = typeof node === "string" ?
document.querySelector(node) : node ?
node : document.body;
let offset = root.childElementCount;
const pix = array.flatMap((img, idx) => {
if (idx >= offset) {
const image = new Image();
const frame = document.createElement("figure");
image.src = `https://i.ibb.co/${img}.png`;
image.className = css;
image.dataset.idx = offset + idx;
root.append(frame.appendChild(image));
return image;
}
return [];
});
return pix;
}
const main = document.forms.gallery;
const io = main.elements;
const areas = Array.from(io.area);
const imgsA = genImg(init, areas[0]);
const imgsB = genImg(init, areas[1]);
imgsA.forEach(img => img.onclick = highlightA);
function highlightA(event) {
this.classList.toggle("highlight");
}
areas[1].onclick = highlightB;
function highlightB(event) {
const clk = event.target;
if (clk.matches("img")) {
clk.classList.toggle("highlight");
}
}
const btns = Array.from(io.add);
btns.forEach(btn => btn.onclick = addImg);
function addImg(event) {
const clk = event.target;
if (clk.matches("button")) {
let idx = btns.indexOf(clk);
genImg(images, areas[idx]);
}
}
const bind = io.bind;
bind.onclick = bindImg;
function bindImg(event) {
Array.from(document.querySelectorAll("#A img"))
.forEach(img => img.onclick = highlightA);
}
html {font: 300 4vmin/1.15 "Segoe UI"}
form {display: flex; flex-flow: column nowrap; justify-content: center;
margin: 15px auto; padding: 0 10px;}
fieldset {margin: 0.5rem 0}
fieldset fieldset {display: flex; justify-content: space-evenly; align-items: center;}
legend {font-size: 1.25rem}
button {font: inherit; float: right; cursor: pointer;}
figure {display: inline-flex; justify-content: center; align-items: center;
margin: 0.5rem 0.5rem 0; padding: 0.5rem;}
.img {display:inline-block; max-width: 5rem}
.highlight {outline: 5px groove cyan;}
<form id="gallery">
<fieldset>
<legend>Area A</legend>
<fieldset id="A" name="area"></fieldset>
<button name="add" type="button">ADD</button>
<button name="bind" type="button">BIND</button>
</fieldset>
<hr>
<fieldset>
<legend>Area B</legend>
<fieldset id="B" name="area"></fieldset>
<button name="add" type="button">ADD</button>
</fieldset>
</form>

Prevent block from touching the containers borders

I have this little block that I move around using javascript code. It works all good except if I keep moving it, it can easily get out of the box where it is supposed to be.
Can I prevent this somehow? So no matter how far I want to move it, it will stay stuck inside of the container/box ?
Here's my snippet code:
/// store key codes and currently pressed ones
var keys = {};
keys.UP = 38;
keys.LEFT = 37;
keys.RIGHT = 39;
keys.DOWN = 40;
/// store reference to character's position and element
var character = {
x: 100,
y: 100,
speedMultiplier: 2,
element: document.getElementById("character")
};
var is_colliding = function(div1, div2) {
var d1_height = div1.offsetHeight;
var d1_width = div1.offsetWidth;
var d1_distance_from_top = div1.offsetTop + d1_height;
var d1_distance_from_left = div1.offsetLeft + d1_width;
var d2_height = div2.offsetHeight;
var d2_width = div2.offsetWidth;
var d2_distance_from_top = div2.offsetTop + d2_height;
var d2_distance_from_left = div2.offsetLeft + d2_width;
var not_colliding =
d1_distance_from_top <= div2.offsetTop ||
div1.offsetTop >= d2_distance_from_top ||
d1_distance_from_left <= div2.offsetTop ||
div1.offsetLeft >= d2_distance_from_left;
return !not_colliding;
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += (dx||0) * character.speedMultiplier;
character.y += (dy||0) * character.speedMultiplier;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-5, 0);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(5, 0);
}
if ( keys[keys.UP] ) {
moveCharacter(0, -5);
}
if ( keys[keys.DOWN] ) {
moveCharacter(0, 5);
}
};
/// update current position on screen
moveCharacter();
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
body{
display: flex;
justify-content: center;
align-items: center;
}
#character {
position: absolute;
width: 42px;
height: 42px;
background: red;
z-index:99;
}
#container{
width: 400px;
height: 400px;
background: transparent;
border:5px solid rgb(0, 0, 0);
position: relative;
overflow: hidden;
}
<div id="container">
<div id="character"></div>
</div>
PS: You can move the box using keyboard arrows.
Get the container width and height into variable and set a condition on your move
var moveCharacter = function(dx, dy){
let div_width = document.getElementById('container').clientWidth;
let div_height = document.getElementById('container').clientHeight;
if((div_width - character.x) < 50 ){ // 50 = width of character and padding
character.x = div_width - 50;
}
if(character.x < 10){ // Padding
character.x = 11;
}
if((div_height - character.y) < 50 ){
character.y = div_height - 50;
}
if(character.y < 10){
character.y = 11;
}

HTML Drag and Drop on touchscreen

I am working on a Task which involves dragging of images and checking where it is dropping and performing action if it is dropped on right location. While it is working absolutely fine on anything that has a mouse it does not work on a touchscreen. How can achieve this goal on a touchscreen. using Vuejs 2 or vanilla javascript
Drag Item
<v-row v-for="(item, iterator) in Activity.drag_items" :key="item.class" :class="[item.class, item.status]" class="drag-item">
<v-img
draggable
#dragstart='startDrag($event, item, iterator)'
:src="require(`#/assets/img/activities/activity_2/${item.item_img}`)"
contain
:class="item.status"
></v-img>
</v-row>
Drop Item
<a #drop='onDrop($event, Activity)' #dragover.prevent #dragenter.prevent></a>
On Drag Function
startDrag(evt, item, index){
evt.dataTransfer.dropEffect = 'move';
evt.dataTransfer.effectAllowed = 'move';
evt.dataTransfer.setData('item', JSON.stringify(item));
evt.dataTransfer.setData('index', index);
}
On Drop Function
onDrop(evt, galaxy_location) {}
As for now, there is no dataTransfer object for touch event. One way to do this is to have methods that copy the value or data and mutate it base on the touch event. In my example I have three methods to simulate the drag and drop with touch
On touch start, I stored the reference that is needed into object, this is similar to dataTransfer.setData(), but added work here is to simulate the feeling of drag and drop by removing item on touchstart and duplicate a new element to follow your touch event
touchstartDrag(e, item, arr) {
// go through origin array
arr.forEach((el, i) => {
if (el == item) {
// store it as reference
this.touchDragItem = {
item: item,
index: i,
arr: arr
}
// remove item in the array, or you can change opacity
arr.splice(i, 1)
}
})
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src = `https://cdn.quasar.dev/img/avatar${this.touchDragItem.item}.jpg`;
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside the image
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
document.getElementById('app').appendChild(image);
},
On touchmove, this is purely to simulate the dragging feeling that you get from dnd, get the element created in touchstart and make it follow your touchmove
touchmoveDrag(e) {
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
this.touchX = e.touches[0].pageX
this.touchY = e.touches[0].pageY
},
On touch end where you define your drop function. Because there is no drop event, you have to manually detect your touch according to the dropzone, if it's outside the dropzone, define your logic, if it's within, execute it accordingly as you define dataTransfer.getData()
touchendDrag(e) {
// remove the image on touch end
let image = document.getElementById('image-float')
image.remove()
// get the dropzone of top and bottom
let rect1 = document.getElementById('top').getBoundingClientRect();
let rect2 = document.getElementById('bottom').getBoundingClientRect()
// to detect the overlap of mouse into the dropzone, as alternative of mouseover
var overlapTop = !(rect1.right < this.touchX ||
rect1.left > this.touchX ||
rect1.bottom < this.touchY ||
rect1.top > this.touchY)
// to detect the overlap of mouse into the dropzone bottom
var overlapBottom = !(rect2.right < this.touchX ||
rect2.left > this.touchX ||
rect2.bottom < this.touchY ||
rect2.top > this.touchY)
// get the stored reference
let ex = this.touchDragItem
// if on touchend the touch is not inside any dropzone, just restore back to the original array
if (!overlapTop && !overlapBottom) {
ex.arr.splice(ex.index, 0, ex.item)
} else {
if (overlapTop) {
if (this.top == ex.arr) {
ex.arr.splice(ex.index, 0, ex.item)
}
if (this.top != ex.arr) {
this.top.push(ex.item)
}
}
if (overlapBottom) {
if (this.bottom == ex.arr) {
ex.arr.splice(ex.index, 0, ex.item)
}
if (this.bottom != ex.arr) {
this.bottom.push(ex.item)
}
}
}
this.touchDragItem = null
},
Overall this is a naive approach to simulate dnd API for touch event, there are plenty of vue.js drag and drop library for you to go, depends on your use case such as sortable.js . But if you want to implement your own touch drag, this is somewhere you can start off
Here is the full working example
new Vue({
el: "#app",
data: {
touchDragItem: null,
touchX: null,
touchY: null,
top: ['1', '2', '3'],
bottom: [],
},
methods: {
dragmouse(e, item) {
e.dataTransfer.dropEffect = 'move'
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('item', item)
},
onDrop(e, pos) {
let arr = pos == 'top' ? 'bottom' : 'top'
let item = e.dataTransfer.getData('item')
this[arr].forEach((el, i) => {
if (el == item) {
this[arr].splice(i, 1)
this[pos].push(el)
}
})
},
touchstartDrag(e, item, arr) {
// go through origin array
arr.forEach((el, i) => {
if (el == item) {
// store it as reference
this.touchDragItem = {
item: item,
index: i,
arr: arr
}
// remove item in the array, or you can change opacity
arr.splice(i, 1)
}
})
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src = `https://cdn.quasar.dev/img/avatar${this.touchDragItem.item}.jpg`;
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside the image
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
document.getElementById('app').appendChild(image);
},
touchmoveDrag(e) {
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
this.touchX = e.touches[0].pageX
this.touchY = e.touches[0].pageY
},
touchendDrag(e) {
// remove the image on touch end
let image = document.getElementById('image-float')
image.remove()
// get the dropzone of top and bottom
let rect1 = document.getElementById('top').getBoundingClientRect();
let rect2 = document.getElementById('bottom').getBoundingClientRect()
// to detect the overlap of mouse into the dropzone, as alternative of mouseover
var overlapTop = !(rect1.right < this.touchX ||
rect1.left > this.touchX ||
rect1.bottom < this.touchY ||
rect1.top > this.touchY)
// to detect the overlap of mouse into the dropzone bottom
var overlapBottom = !(rect2.right < this.touchX ||
rect2.left > this.touchX ||
rect2.bottom < this.touchY ||
rect2.top > this.touchY)
// get the stored reference
let ex = this.touchDragItem
// if on touchend the touch is not inside any dropzone, just restore back to the original array
if (!overlapTop && !overlapBottom) {
ex.arr.splice(ex.index, 0, ex.item)
} else {
if (overlapTop) {
if (this.top == ex.arr) {
ex.arr.splice(ex.index, 0, ex.item)
}
if (this.top != ex.arr) {
this.top.push(ex.item)
}
}
if (overlapBottom) {
if (this.bottom == ex.arr) {
ex.arr.splice(ex.index, 0, ex.item)
}
if (this.bottom != ex.arr) {
this.bottom.push(ex.item)
}
}
}
this.touchDragItem = null
},
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
button {
color: #4fc08d;
}
button {
background: none;
border: solid 1px;
border-radius: 2em;
font: inherit;
padding: 0.75em 2em;
}
.dropzone {
display: flex;
height: fit-content;
min-width: 50px;
min-height: 50px;
background: #2D2D2D;
margin: 10px;
padding: 10px;
}
.dropzone>* {
margin: 0 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="top" class="dropzone" #drop="onDrop($event,'top')" #dragenter.prevent #dragover.prevent>
<img ref="image" draggable #dragstart="dragmouse($event,item)" #touchstart.prevent="touchstartDrag($event,item,top)" #touchmove="touchmoveDrag" #touchend="touchendDrag" :src="`https://cdn.quasar.dev/img/avatar${item}.jpg`" width="100" height="100" v-for="item in top"
:key="item" />
</div>
<div id="bottom" class="dropzone" #drop="onDrop($event,'bottom')" #dragenter.prevent #dragover.prevent>
<img ref="image" draggable #dragstart="dragmouse($event,item)" #touchstart.prevent="touchstartDrag($event,item,bottom)" #touchmove="touchmoveDrag" #touchend="touchendDrag" :src="`https://cdn.quasar.dev/img/avatar${item}.jpg`" width="100" height="100"
v-for="item in bottom" :key="item" />
</div>
</div>

Loop for onmousemove event

I've run into a problem with running a loop to trigger a mousemove event on my HTML/CSS.
I know I can go through and get every individual ID on the HTML tags to execute the code the way I want. But I know there is a better way to do it with a loop of some sort and use far less code.
The images should follow the mouse while moving over the div with class mycard. Any suggestions or ideas on how to get it working properly would be very much appreciated.
I've tried running a loop to add the classes to divs but had no luck.
var mouseHover = document.getElementById('moveHover');
window.onmousemove = function(e) {
var x = e.clientX;
var y = e.clientY;
mouseHover.style.top = (y + 20) + 'px';
mouseHover.style.left = (x + 20) + 'px';
};
.mycard span {
position: absolute;
display: none;
z-index: 99;
}
.mycard:hover span {
display: block;
position: fixed;
overflow: hidden;
}
.imgHover a {
position: relative;
}
.imgHover span {
position: absolute;
display: none;
z-index: 99;
}
.imgHover a:hover span {
display: block;
position: fixed;
overflow: hidden;
}
<div class="imgHover mycard">
<div class="cardcost">
<p class="cardcosttext">2</p>
</div>
<div class="hscardepic">
<a style="margin-left: 1000%;vertical-align: middle;">
Doomsayer
<span id="moveHover">
<img src="Classic_Set/doomsayer.png" height="300" width="300" />
</span>
</a>
</div>
<div class="cardamount">
<p class="cardamounttext">×2</p>
</div>
</div>
If I understand what you're asking, you could use querySelectorAll to get the elements and forEach to move them:
// get the div that responds to mouse movement
const mycard = document.querySelector('.mycard');
// add a mousemove listener
mycard.addEventListener('mousemove', function(e) {
// get the DOM element with the mousemove listener from the event
const {target} = e;
// get img child elements of the target.
// (use whatever css selector you need here. doesn't have to img)
const images = target.querySelectorAll('img');
// iterate over each item...
images.forEach(image => {
// ...and do whatever you need to do with it
const x = e.clientX;
const y = e.clientY;
image.style.top = (y + 20) + 'px';
image.style.left = (x + 20) + 'px';
})
});
I'm also not entirely sure what your end-goal is, but I'll take a stab at it.
I would recommend changing moveHover to being the class instead of the ID. Then you could do something like this:
var mouseHover = null;
window.onmousemove = function (e) {
if(mouseHover != null){
var x = e.clientX;
var y = e.clientY;
mouseHover.style.top = (y+20) + 'px';
mouseHover.style.left = (x+20) + 'px';
}
};
function onHover(e){
mouseHover = e.target.querySelector('.moveHover');
}
var elements = document.getElementsByClassName('imgHover');
for(var i = 0; i < elements.length; i++){
elements[i].onmouseenter = onHover;
}
The loop runs one time to set the onmouseenter event. Sure beats moving all .moveHover elements all the time.

Javascript Drag and drop

I'm looking for someone to explain how to drag and drop in javascript, I want a horizontal line with some customizable images in it.
I've had a look at the online tutorials for these but find them very hard to use.
I would recommend that you look into one of the Javascript Frameworks out there. We use prototype with scriptaculous.
You can look at a demo for Drag and Drop in Scriptaculous here. And you can run through a tutorial here.
Or look into any of the other frameworks such as JQuery or Dojo.
Here is my code as it is...I have used this page to get this far..
It currently moves everything into column one, I think it's something to do with the mouse.
print("<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Page</title>
<--
<style type="text/css">
<!--
.DragContainer, .OverDragContainer {
float: left;
margin: 3px;
width: 100px;
border: #669999 2px solid;
padding: 5px;
}
.DragBox, .OverDragBox, .DragDragBox, .miniDragBox {
border: #000 1px solid;
padding: 2px;
font-size: 10px;
margin-bottom: 5px;
width: 94px;
cursor: pointer;
font-family: verdana, tahoma, arial;
background-color: #eee;
}
.OverDragContainer {
background-color: #eee;
}
.OverDragBox, .DragDragBox {
background-color: #ffff99;
}
.DragDragBox {
filter: alpha(opacity=50);
background-color: #ff99cc;
}
legend {
font-weight: bold;
font-size: 12px;
color: #666699;
font-family: verdana, tahoma, arial;
}
fieldset {
padding: 3px;
}
.History {
font-size: 10px;
overflow: auto;
width: 100%;
font-family: verdana, tahoma, arial;
height: 82px;
}
#DragContainer8 {
border: #669999 1px solid;
padding: 5px 0 0 5px
width: 110px;
height: 40px;
}
.miniDragBox {
float: left;
margin: 0 5px 5px 0;
width: 20px;
height: 20px;
}
-->
</style>
<--script type="text/javascript">
// iMouseDown represents the current mouse button state: up or down
/*
lMouseState represents the previous mouse button state so that we can
check for button clicks and button releases:
if(iMouseDown && !lMouseState) // button just clicked!
if(!iMouseDown && lMouseState) // button just released!
*/
var mouseOffset = null;
var iMouseDown = false;
var lMouseState = false;
var dragObject = null;
// Demo 0 variables
var DragDrops = [];
var curTarget = null;
var lastTarget = null;
var dragHelper = null;
var tempDiv = null;
var rootParent = null;
var rootSibling = null;
Number.prototype.NaN0=function(){return isNaN(this)?0:this;}
function CreateDragContainer(){
/*
Create a new "Container Instance" so that items from one "Set" can not
be dragged into items from another "Set"
*/
var cDrag = DragDrops.length;
DragDrops[cDrag] = [];
/*
Each item passed to this function should be a "container". Store each
of these items in our current container
*/
for(var i=0; i<arguments.length; i++){
var cObj = arguments[i];
DragDrops[cDrag].push(cObj);
cObj.setAttribute('DropObj', cDrag);
/*
Every top level item in these containers should be draggable. Do this
by setting the DragObj attribute on each item and then later checking
this attribute in the mouseMove function
*/
for(var j=0; j<cObj.childNodes.length; j++){
// Firefox puts in lots of #text nodes...skip these
if(cObj.childNodes[j].nodeName=='#text') continue;
cObj.childNodes[j].setAttribute('DragObj', cDrag);
}
}
}
function getMouseOffset(target, ev){
ev = ev || window.event;
var docPos = getPosition(target);
var mousePos = mouseCoords(ev);
return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
}
function getPosition(e){
var left = 0;
var top = 0;
while (e.offsetParent){
left += e.offsetLeft;
top += e.offsetTop;
e = e.offsetParent;
}
left += e.offsetLeft;
top += e.offsetTop;
return {x:left, y:top};
}
function makeDraggable(item){
if(!item) return;
item.onmousedown = function(ev){
dragObject = this;
mouseOffset = getMouseOffset(this, ev);
return false;
}
}
function makeClickable(object){
object.onmousedown = function(){
dragObject = this;
}
}
function mouseCoords(ev){
if(ev.pageX || ev.pageY){
return {x:ev.pageX, y:ev.pageY};
}
return {
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
}
function mouseMove(ev){
ev = ev || window.event;
/*
We are setting target to whatever item the mouse is currently on
Firefox uses event.target here, MSIE uses event.srcElement
*/
var target = ev.target || ev.srcElement;
var mousePos = mouseCoords(ev);
// mouseOut event - fires if the item the mouse is on has changed
if(lastTarget && (target!==lastTarget)){
// reset the classname for the target element
var origClass = lastTarget.getAttribute('origClass');
if(origClass) lastTarget.className = origClass;
}
/*
dragObj is the grouping our item is in (set from the createDragContainer function).
if the item is not in a grouping we ignore it since it can't be dragged with this
script.
*/
var dragObj = target.getAttribute('DragObj');
// if the mouse was moved over an element that is draggable
if(dragObj!=null){
// mouseOver event - Change the item's class if necessary
if(target!=lastTarget){
var oClass = target.getAttribute('overClass');
if(oClass){
target.setAttribute('origClass', target.className);
target.className = oClass;
}
}
// if the user is just starting to drag the element
if(iMouseDown && !lMouseState){
// mouseDown target
curTarget = target;
// Record the mouse x and y offset for the element
rootParent = curTarget.parentNode;
rootSibling = curTarget.nextSibling;
mouseOffset = getMouseOffset(target, ev);
// We remove anything that is in our dragHelper DIV so we can put a new item in it.
for(var i=0; i<dragHelper.childNodes.length; i++) dragHelper.removeChild(dragHelper.childNodes[i]);
// Make a copy of the current item and put it in our drag helper.
dragHelper.appendChild(curTarget.cloneNode(true));
dragHelper.style.display = 'block';
// set the class on our helper DIV if necessary
var dragClass = curTarget.getAttribute('dragClass');
if(dragClass){
dragHelper.firstChild.className = dragClass;
}
// disable dragging from our helper DIV (it's already being dragged)
dragHelper.firstChild.removeAttribute('DragObj');
/*
Record the current position of all drag/drop targets related
to the element. We do this here so that we do not have to do
it on the general mouse move event which fires when the mouse
moves even 1 pixel. If we don't do this here the script
would run much slower.
*/
var dragConts = DragDrops[dragObj];
/*
first record the width/height of our drag item. Then hide it since
it is going to (potentially) be moved out of its parent.
*/
curTarget.setAttribute('startWidth', parseInt(curTarget.offsetWidth));
curTarget.setAttribute('startHeight', parseInt(curTarget.offsetHeight));
curTarget.style.display = 'none';
// loop through each possible drop container
for(var i=0; i<dragConts.length; i++){
with(dragConts[i]){
var pos = getPosition(dragConts[i]);
/*
save the width, height and position of each container.
Even though we are saving the width and height of each
container back to the container this is much faster because
we are saving the number and do not have to run through
any calculations again. Also, offsetHeight and offsetWidth
are both fairly slow. You would never normally notice any
performance hit from these two functions but our code is
going to be running hundreds of times each second so every
little bit helps!
Note that the biggest performance gain here, by far, comes
from not having to run through the getPosition function
hundreds of times.
*/
setAttribute('startWidth', parseInt(offsetWidth));
setAttribute('startHeight', parseInt(offsetHeight));
setAttribute('startLeft', pos.x);
setAttribute('startTop', pos.y);
}
// loop through each child element of each container
for(var j=0; j<dragConts[i].childNodes.length; j++){
with(dragConts[i].childNodes[j]){
if((nodeName=='#text') || (dragConts[i].childNodes[j]==curTarget)) continue;
var pos = getPosition(dragConts[i].childNodes[j]);
// save the width, height and position of each element
setAttribute('startWidth', parseInt(offsetWidth));
setAttribute('startHeight', parseInt(offsetHeight));
setAttribute('startLeft', pos.x);
setAttribute('startTop', pos.y);
}
}
}
}
}
// If we get in here we are dragging something
if(curTarget){
// move our helper div to wherever the mouse is (adjusted by mouseOffset)
dragHelper.style.top = mousePos.y - mouseOffset.y;
dragHelper.style.left = mousePos.x - mouseOffset.x;
var dragConts = DragDrops[curTarget.getAttribute('DragObj')];
var activeCont = null;
var xPos = mousePos.x - mouseOffset.x + (parseInt(curTarget.getAttribute('startWidth')) /2);
var yPos = mousePos.y - mouseOffset.y + (parseInt(curTarget.getAttribute('startHeight'))/2);
// check each drop container to see if our target object is "inside" the container
for(var i=0; i<dragConts.length; i++){
with(dragConts[i]){
if(((getAttribute('startLeft')) < xPos) &&
((getAttribute('startTop')) < yPos) &&
((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &&
((getAttribute('startTop') + getAttribute('startHeight')) > yPos)){
/*
our target is inside of our container so save the container into
the activeCont variable and then exit the loop since we no longer
need to check the rest of the containers
*/
activeCont = dragConts[i];
// exit the for loop
break;
}
}
}
// Our target object is in one of our containers. Check to see where our div belongs
if(activeCont){
// beforeNode will hold the first node AFTER where our div belongs
var beforeNode = null;
// loop through each child node (skipping text nodes).
for(var i=activeCont.childNodes.length-1; i>=0; i--){
with(activeCont.childNodes[i]){
if(nodeName=='#text') continue;
// if the current item is "After" the item being dragged
if(
curTarget != activeCont.childNodes[i] &&
((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &&
((getAttribute('startTop') + getAttribute('startHeight')) > yPos)){
beforeNode = activeCont.childNodes[i];
}
}
}
// the item being dragged belongs before another item
if(beforeNode){
if(beforeNode!=curTarget.nextSibling){
activeCont.insertBefore(curTarget, beforeNode);
}
// the item being dragged belongs at the end of the current container
} else {
if((curTarget.nextSibling) || (curTarget.parentNode!=activeCont)){
activeCont.appendChild(curTarget);
}
}
// make our drag item visible
if(curTarget.style.display!=''){
curTarget.style.display = '';
}
} else {
// our drag item is not in a container, so hide it.
if(curTarget.style.display!='none'){
curTarget.style.display = 'none';
}
}
}
// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;
// mouseMove target
lastTarget = target;
// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;
// this helps prevent items on the page from being highlighted while dragging
return false;
}
function mouseUp(ev){
if(curTarget){
// hide our helper object - it is no longer needed
dragHelper.style.display = 'none';
// if the drag item is invisible put it back where it was before moving it
if(curTarget.style.display == 'none'){
if(rootSibling){
rootParent.insertBefore(curTarget, rootSibling);
} else {
rootParent.appendChild(curTarget);
}
}
// make sure the drag item is visible
curTarget.style.display = '';
}
curTarget = null;
iMouseDown = false;
}
function mouseDown(){
iMouseDown = true;
if(lastTarget){
return false;
}
}
document.onmousemove = mouseMove;
document.onmousedown = mouseDown;
document.onmouseup = mouseUp;
window.onload = function(){
// Create our helper object that will show the item while dragging
dragHelper = document.createElement('DIV');
dragHelper.style.cssText = 'position:absolute;display:none;';
CreateDragContainer(
document.getElementById('DragContainer1'),
document.getElementById('DragContainer2'),
document.getElementById('DragContainer3')
);
document.body.appendChild(dragHelper);
}
</script><!--the mouse over and dragging class are defined on each item-->
</head>
<body>
<br/>
<div class="DragContainer" id="DragContainer1">
<div class="DragBox" id="Item1" overClass="OverDragBox" dragClass="DragDragBox">Item #1</div>
<div class="DragBox" id="Item2" overClass="OverDragBox" dragClass="DragDragBox">Item #2</div>
<div class="DragBox" id="Item3" overClass="OverDragBox" dragClass="DragDragBox">Item #3</div>
<div class="DragBox" id="Item4" overClass="OverDragBox" dragClass="DragDragBox">Item #4</div>
</div>
<div class="DragContainer" id="DragContainer2">
<div class="DragBox" id="Item5" overClass="OverDragBox" dragClass="DragDragBox">Item #5</div>
<div class="DragBox" id="Item6" overClass="OverDragBox" dragClass="DragDragBox">Item #6</div>
<div class="DragBox" id="Item7" overClass="OverDragBox" dragClass="DragDragBox">Item #7</div>
<div class="DragBox" id="Item8" overClass="OverDragBox" dragClass="DragDragBox">Item #8</div>
</div>
<div class="DragContainer" id="DragContainer3">
<div class="DragBox" id="Item9" overClass="OverDragBox" dragClass="DragDragBox">Item #9</div>
<div class="DragBox" id="Item10" overClass="OverDragBox" dragClass="DragDragBox">Item #10</div>
<div class="DragBox" id="Item11" overClass="OverDragBox" dragClass="DragDragBox">Item #11</div>
<div class="DragBox" id="Item12" overClass="OverDragBox" dragClass="DragDragBox">Item #12</div>
</div>
<br/>
</body>
</html>
");
Find 2x of:
dragHelper.style.top = mousePos.y - mouseOffset.y;
dragHelper.style.left = mousePos.x - mouseOffset.x;
change to:
dragHelper.style.top = mousePos.y - mouseOffset.y + 'px';
dragHelper.style.left = mousePos.x - mouseOffset.x + 'px';
Works on FF. 3.6
the answer is here jQuery drag-and-drop DIV selection with images and text
<script type="text/javascript" src="http://www.dynamicdrive.com/dynamicindex11/domdrag/dom-drag.js"></script>
<script type="text/javascript">
Drag.init(document.getElementById("exampleid")); //sets the id to look for to make object draggable
</script>

Categories

Resources