Can't drag and drop to another div using interact.js - javascript

Html/css newbie here,
I am using interact.js to attain a certain UI where I can drop items from a list into a drop zone. The problem is my list can grow very long and I need to add scroll to the list, and when I try to do so I have create a 'div' and put the list items into it to show the scroll behaviour.
But when I put the list items inside a div and the drop zone outside it, the items do not get dropped there and the list shows a weird behaviour, all of them starts getting stacked on top of the bottom list item (at the bottom of the div containing the items).
This is my html:
<div class="items">
<p style="padding-top: 20px; font-size:18px">Stacked Items</p>
<div style = "height: 400px; overflow: scroll">
<div id="itemstodrop" class="drag-drop"> item 1 </div>
<div id="itemstodrop" class="drag-drop"> item 2 </div>
<div id="itemstodrop" class="drag-drop"> item 3 </div>
<div id="itemstodrop" class="drag-drop"> item 4 </div>
<div id="itemstodrop" class="drag-drop"> item 5 </div>
<div id="itemstodrop" class="drag-drop"> item 6 </div>
<div id="itemstodrop" class="drag-drop"> item 7 </div>
<div id="itemstodrop" class="drag-drop"> item 8 </div>
<div id="itemstodrop" class="drag-drop"> item 9 </div>
<div id="itemstodrop" class="drag-drop"> item 10 </div>
<div id="itemstodrop" class="drag-drop"> item 11 </div>
<div id="itemstodrop" class="drag-drop"> item 12 </div>
</div>
<div id="inner-dropzone" class="dropzone">Drop here</div>
</div>
This is my script which holds interact.js code:
import interact from
'https://cdn.jsdelivr.net/npm/#interactjs/interactjs/index.js'
// enable draggables to be dropped into this
var startPos = null;
//define the initial position for each rig tile.
function dragMoveListener (event) {
var target = event.target
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
// this function is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener
//ALL THE FUNCTIONS BELOW TALK ABOUT THE DRAGGABLE AREA.
interact('.dropzone').dropzone({
// only accept elements matching this CSS selector
accept: '#itemstodrop',
// the item should go 100% inside the draggable area...
overlap: 0.9999999, //somehow it does not take the value 100%
// listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback
event.target.classList.add('drop-active')
},
ondragenter: function (event) {
//when item enters the draggable area
var draggableElement = event.relatedTarget
var dropzoneElement = event.target
console.log(draggableElement.firstChild.nodeValue);
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target')
draggableElement.classList.add('can-drop')
},
ondragleave: function (event) {
console.log(startPos)
event.draggable.draggable({
snap: {
targets: [startPos]
}
});
//when item leaves the draggable area
// remove the drop feedback style
event.target.classList.remove('drop-target')
event.relatedTarget.classList.remove('can-drop')
},
ondrop: function (event) {
//when the item is droppd in the draggable area
{% comment %} event.relatedTarget.textContent = 'Dropped' {% endcomment %}
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active')
event.target.classList.remove('drop-target')
}
})
interact('.drag-drop')
.draggable({
inertia: true,
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent',
endOnly: true
})
],
autoScroll: true,
// dragMoveListener from the dragging demo above
listeners: { move: dragMoveListener }
})
</script>
This is how it looks when rendered: (Basically can't drop any item to drop area.)
Full working page for demo:
(I have added a comment which tells where have I put the div.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#outer-dropzone {
height: 140px;
}
#inner-dropzone {
height: 80px;
}
.dropzone {
background-color: #ccc;
border: dashed 4px transparent;
border-radius: 4px;
margin: 10px auto 30px;
padding: 10px;
width: 80%;
transition: background-color 0.3s;
}
.drop-active {
border-color: #aaa;
}
.drop-target {
background-color: #29e;
border-color: #fff;
border-style: solid;
}
.drag-drop {
display: inline-block;
min-width: 40px;
padding: 2em 0.5em;
color: #fff;
background-color: #29e;
border: solid 2px #fff;
touch-action: none;
-webkit-transform: translate(0px, 0px);
transform: translate(0px, 0px);
transition: background-color 0.3s;
}
.drag-drop.can-drop {
color: #000;
background-color: #4e4;
}
</style>
</head>
<body>
{% comment %} I NEED THIS DIV UNDER THIS COMMENT, TO ADD A SCROLL AREA. BUT WHEN I GIVE THIS DIV, THE DROP DOES NOT OCCUR. {% endcomment %}
<div>
<div id="no-drop" class="drag-drop"> #no-drop </div>
<div id="yes-drop" class="drag-drop"> #yes-drop </div>
</div>
<div id="outer-dropzone" class="dropzone">
#outer-dropzone
<div id="inner-dropzone" class="dropzone">#inner-dropzone</div>
</div>
<script type="module">
import interact from
'https://cdn.jsdelivr.net/npm/#interactjs/interactjs/index.js'
// enable draggables to be dropped into this
interact('.draggable')
.draggable({
// enable inertial throwing
inertia: true,
// keep the element within the area of it's parent
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent',
endOnly: true
})
],
// enable autoScroll
autoScroll: true,
listeners: {
// call this function on every dragmove event
move: dragMoveListener,
// call this function on every dragend event
end (event) {
var textEl = event.target.querySelector('p')
textEl && (textEl.textContent =
'moved a distance of ' +
(Math.sqrt(Math.pow(event.pageX - event.x0, 2) +
Math.pow(event.pageY - event.y0, 2) | 0))
.toFixed(2) + 'px')
}
}
})
function dragMoveListener (event) {
var target = event.target
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
// this function is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener
//ALL THE FUNCTIONS BELOW TALK ABOUT THE DRAGGABLE AREA.
interact('.dropzone').dropzone({
// only accept elements matching this CSS selector
accept: '#yes-drop',
// the item should go 100% inside the draggable area...
overlap: 0.9999999, //somehow it does not take the value 100%
// listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback
event.target.classList.add('drop-active')
},
ondragenter: function (event) {
//when item enters the draggable area
var draggableElement = event.relatedTarget
var dropzoneElement = event.target
console.log(draggableElement.firstChild.nodeValue);
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target')
draggableElement.classList.add('can-drop')
},
ondragleave: function (event) {
//when item leaves the draggable area
// remove the drop feedback style
event.target.classList.remove('drop-target')
event.relatedTarget.classList.remove('can-drop')
},
ondrop: function (event) {
//when the item is droppd in the draggable area
{% comment %} event.relatedTarget.textContent = 'Dropped' {% endcomment %}
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active')
event.target.classList.remove('drop-target')
}
})
interact('.drag-drop')
.draggable({
inertia: true,
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent',
endOnly: true
})
],
autoScroll: true,
// dragMoveListener from the dragging demo above
listeners: { move: dragMoveListener }
})
</script>
</body>
</html>

You need to change accept: '#rigstodrop' to accept: '#itemstodrop' because here your div has id itemstodrop which you need to drag and drop . Also , use restriction: '.items' to restrict rect not to go beyond that area .
Demo code :
var startPos = null;
//define the initial position for each rig tile.
function dragMoveListener(event) {
var target = event.target
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
// this function is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener
interact('.dropzone').dropzone({
// only accept elements matching this CSS selector
accept: '#itemstodrop', //change this
// the item should go 100% inside the draggable area...
overlap: 0.9999999,
ondropactivate: function(event) {
// add active dropzone feedback
event.target.classList.add('drop-active')
},
ondragenter: function(event) {
//when item enters the draggable area
var draggableElement = event.relatedTarget
var dropzoneElement = event.target
console.log(draggableElement.firstChild.nodeValue);
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target')
draggableElement.classList.add('can-drop')
},
ondragleave: function(event) {
console.log(startPos)
event.draggable.draggable({
snap: {
targets: [startPos]
}
});
//when item leaves the draggable area
// remove the drop feedback style
event.target.classList.remove('drop-target')
event.relatedTarget.classList.remove('can-drop')
},
ondrop: function(event) {
//when the item is droppd in the draggable area
event.relatedTarget.textContent = 'Dropped'
},
ondropdeactivate: function(event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active')
event.target.classList.remove('drop-target')
}
})
interact('.drag-drop')
.draggable({
inertia: true,
modifiers: [
interact.modifiers.restrictRect({
restriction: '.items', //change this
endOnly: true
})
],
autoScroll: true,
// dragMoveListener from the dragging demo above
listeners: {
move: dragMoveListener
}
})
.itemstodrop {
touch-action: none;
user-select: none;
}
#inner-dropzone {
height: 80px;
}
.dropzone {
background-color: #ccc;
border: dashed 4px transparent;
border-radius: 4px;
margin: 10px auto 30px;
padding: 10px;
width: 80%;
transition: background-color 0.3s;
}
.drop-active {
border-color: #aaa;
}
.drop-target {
background-color: #29e;
border-color: #fff;
border-style: solid;
}
.drag-drop {
display: inline-block;
min-width: 40px;
padding: 2em 0.5em;
color: #fff;
background-color: #29e;
border: solid 2px #fff;
touch-action: none;
-webkit-transform: translate(0px, 0px);
transform: translate(0px, 0px);
transition: background-color 0.3s;
}
.drag-drop.can-drop {
color: #000;
background-color: #4e4;
}
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
<div class="items">
<p style="padding-top: 20px; font-size:18px">Stacked Rigs</p>
<div style="height: auto;overflow: scroll">
<div id="itemstodrop" class="drag-drop itemstodrop"> item 1 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 2 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 3 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 4 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 5 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 6 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 7 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 8 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 9 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 10 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 11 </div>
<div id="itemstodrop" class="drag-drop itemstodrop"> item 12 </div>
<!--put this inside div-->
<div id="inner-dropzone" class="dropzone">Drop here</div>
</div>
</div>

Related

How to make dynamically created elements draggable with gridstack?

In my project I'm using this drag and drop library called gridstack. You can see their documentation on github here. When you hardcode elements inside the dom and initialize the gridstack, those elements are draggable. But when the elements are created dynamically with a forloop, they are not draggable even if they have the proper draggable classes. How can I make this work?
//initialize grid stack
var grid = GridStack.init({
minRow: 5, // don't collapse when empty
cellHeight: 70,
acceptWidgets: true,// acceptWidgets - accept widgets dragged from other grids or from outside (default: false).
dragIn: '.newWidget', // class that can be dragged from outside
dragInOptions: { revert: 'invalid', scroll: false, appendTo: 'body', helper:'clone' }, // clone or can be your function
removable: '#trash', // drag-out delete class
});
//gridstack on change
grid.on('added removed change', function(e, items) {
let str = '';
items.forEach(function(item) { str += ' (x,y)=' + item.x + ',' + item.y; });
console.log(e.type + ' ' + items.length + ' items:' + str );
});
//dynamic elemenets
const arr = ["Bruh cant drag me!", "Nope me neither", "Me too mouhahaha"];
//loop
for (let i = 0; i < arr.length; i++) {
var div = document.createElement('div');
var el = "<div class='newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide'> <div class='grid-stack-item-content' style='padding: 5px;'> "+arr[i]+"</div></div>";
div.innerHTML = el;
$('.dynamic').append(div);
}
.grid-stack-item-removing {
opacity: 0.8;
filter: blur(5px);
}
#trash {
background: rgba(255, 0, 0, 0.4);
padding: 5px;
text-align: center;
}
.grid-stack-item{
background: whitesmoke;
width: 50%;
border: 1px dashed grey;
}
.grid-stack {
background : #F0FFC0;
}
<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!-- gridstack-->
<link rel="stylesheet" href="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-extra.min.css"/>
<script src="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-h5.js"></script>
<!-- body-->
<body>
<div id="trash"><span>drop here to remove</span> </div>
<br>
<div class="dynamic"></div>
<div class="newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide">
<div class="grid-stack-item-content" style="padding: 5px;">
<div>
</div>
<div>
<span>I'm original domster! Drag and drop me!</span>
</div>
</div>
</div>
<br>
<div class="grid-stack"></div>
</body>
This issue was raised here. The grid does not automatically track external changes so the grid needs to be re-initialize for the dragIn option to notice the new dynamic widgets
GridStack.setupDragIn()
Full code
//initialize grid stack
var grid = GridStack.init({
minRow: 5, // don't collapse when empty
cellHeight: 70,
acceptWidgets: true,// acceptWidgets - accept widgets dragged from other grids or from outside (default: false).
dragIn: '.newWidget', // class that can be dragged from outside
dragInOptions: { revert: 'invalid', scroll: false, appendTo: 'body', helper:'clone' }, // clone or can be your function
removable: '#trash', // drag-out delete class
});
//gridstack on change
grid.on('added removed change', function(e, items) {
let str = '';
items.forEach(function(item) { str += ' (x,y)=' + item.x + ',' + item.y; });
console.log(e.type + ' ' + items.length + ' items:' + str );
});
//dynamic elemenets
const arr = ["Bruh cant drag me!", "Nope me neither", "Me too mouhahaha"];
//loop
for (let i = 0; i < arr.length; i++) {
var div = document.createElement('div');
var el = "<div class='newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide'> <div class='grid-stack-item-content' style='padding: 5px;'> "+arr[i]+"</div></div>";
div.innerHTML = el;
$('.dynamic').append(div);
}
GridStack.setupDragIn(
'.newWidget',
);
.grid-stack-item-removing {
opacity: 0.8;
filter: blur(5px);
}
#trash {
background: rgba(255, 0, 0, 0.4);
padding: 5px;
text-align: center;
}
.grid-stack-item{
background: whitesmoke;
width: 50%;
border: 1px dashed grey;
}
.grid-stack {
background : #F0FFC0;
}
<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!-- gridstack-->
<link rel="stylesheet" href="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-extra.min.css"/>
<script src="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-h5.js"></script>
<!-- body-->
<body>
<div id="trash"><span>drop here to remove</span> </div>
<br>
<div class="dynamic"></div>
<div class="newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide">
<div class="grid-stack-item-content" style="padding: 5px;">
<div>
</div>
<div>
<span>I'm original domster! Drag and drop me!</span>
</div>
</div>
</div>
<br>
<div class="grid-stack"></div>
</body>

Stacking multiple Text elements in a single Drag and Drop element using z-index property

I have twelve arguments that i am trying to stack in no particular order on one Drag and drop element.The end user should be able to drag these texts and drop them in a matching element. The problem is the overlapping texts seem to have warped on each other hence the various titles to be dragged from the stack are no longer clearly visible. Does anyone know how to stack texts using z-index property? I have attached an image.
Here is my code
#argumente1,
#argumente2,
#argumente3,
#argumente4,
#argumente5,
#argumente6,
#argumente7,
#argumente8,
#argumente9,
#argumente10,
#argumente11,
#argumente12{
position: absolute;
margin-left:500px;
width:280px;
height:60px;
}
#argumente1{
z-index: 12;
}
#argumente2{
z-index: 11;
}
/*goes on until all twelve id's are defined*/
<div class="ansicht">
<div class="b_dotted" id="argumente1" draggable="true" ondragstart="drag(event)">Firt argument</div>
<div class="b_dotted" id="argumente2" draggable="true" ondragstart="drag(event)">second argument</div>
<div class="b_dotted" id="argumente3" draggable="true" ondragstart="drag(event)">Third of twelve arguments</div>
</div>
while at it please also help me look at the corresponding Javascript that is also not responsive
function dragstart_handler(ev){
//add elements id to the data transfer object
ev.dataTransfer.setData("text/plain",ev.target.id);
}
window.addEventListener('DOMContentLoaded',() => {
// Get element by id
const element = document.querySelectorAll ("#argumente1,#argumente2,#argumente3,#argumente4,#argumente5,#argumente6,#argumente7,#argumente8,#argumente9,#argumente10,#argumente11,#argumente12");
// Add on drag start event listener
element.addEventListener("dragstart",dragstart_handler);
});
function dragstart_handler(ev){
//Add drag data
ev.dataTransfer.setData("text/plain", ev.target.innerText);
}
function dragover_handler(ev){
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
}
function drop_handler(ev){
ev.preventDefault()
// Get the id of the target and add the moved element to the target's DOM
const data = ev.dataTransfer.getData("text/plain");
ev.target.appendChild(document.getElementById(data))
}
Probably not what you looked for, but for infos and shorter CSS declaration
You may ommit the z-index property reset if you use the natural stacking context, which puts the last one on top.
You can also , avoid position if you use a grid and stack them inside the same grid cell:
exmple with the background-color, so you can pick them one after the other
var dragged;
/* events fired on the draggable target */
document.addEventListener("drag", function( event ) {
}, false);
document.addEventListener("dragstart", function( event ) {
// store a ref. on the dragged elem
dragged = event.target;
// make it half transparent
event.target.style.opacity = .5;
}, false);
document.addEventListener("dragend", function( event ) {
// reset the transparency
event.target.style.opacity = "";
}, false);
/* events fired on the drop targets */
document.addEventListener("dragover", function( event ) {
// prevent default to allow drop
event.preventDefault();
}, false);
document.addEventListener("dragenter", function( event ) {
// highlight potential drop target when the draggable element enters it
if ( event.target.className == "dropzone" ) {
event.target.style.background = "purple";
}
}, false);
document.addEventListener("dragleave", function( event ) {
// reset background of potential drop target when the draggable element leaves it
if ( event.target.className == "dropzone" ) {
event.target.style.background = "";
}
}, false);
document.addEventListener("drop", function( event ) {
// prevent default action (open as link for some elements)
event.preventDefault();
// move dragged elem to the selected drop target
if ( event.target.className == "dropzone" ) {
event.target.style.background = "";
dragged.parentNode.removeChild( dragged );
event.target.appendChild( dragged );
}
}, false);
.b_dotted {
width: 280px;
height: 60px;
background: white;
border: solid;
grid-row: 1;/* first row*/
grid-column: -1;/* last cell if number of column is unknown */
cursor: pointer;
}
.ansicht {
display: grid;
grid-template-columns: 1fr auto;
justify-content: end;
border: double;
}
.bis {
grid-template-columns: auto;
/* reset to a single col */
width: max-content;
float: right;
/* ?? useful ? */
}
<div class="ansicht">
Is that container use for something else ? , if no, remove that text and the grid-template-columns rule from the CSS;
<div class="b_dotted" id="argumente43"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">fourth of twelve arguments</div>
<div class="b_dotted" id="argumente3"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">Third of twelve arguments</div>
<div class="b_dotted" id="argumente2"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">second argument</div>
<div class="b_dotted" id="argumente1"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">Firt argument</div>
</div>
<div class="dropzone">
Dropzone:
</div>
<p>similar approach from a single cell and hooked and floated to the right side.</p>
<div class="ansicht bis">
<div class="b_dotted" id="argumente43"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">fourth of twelve arguments</div>
<div class="b_dotted" id="argumente3"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">Third of twelve arguments</div>
<div class="b_dotted" id="argumente2"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">second argument</div>
<div class="b_dotted" id="argumente1"draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">Firt argument</div>
</div>
as you used position: absolute; to all argument and it's mandatory, that's why it's overlapping among themselves. you should fix all the arguments position by defining.
#argumente1,
#argumente2,
#argumente3,
#argumente4,
#argumente5,
#argumente6,
#argumente7,
#argumente8,
#argumente9,
#argumente10,
#argumente11,
#argumente12{
position: absolute;
margin-left:500px;
width:280px;
height:60px;
}
#argumente1{
z-index: 12;
}
#argumente2{
z-index: 11;
top: 24px;
}
#argumente3{
top: 43px;
}
<div class="ansicht">
<div class="b_dotted" id="argumente1" draggable="true" ondragstart="drag(event)">Firt argument</div>
<div class="b_dotted" id="argumente2" draggable="true" ondragstart="drag(event)">second argument</div>
<div class="b_dotted" id="argumente3" draggable="true" ondragstart="drag(event)">Third of twelve arguments</div>
</div>

Tooltipster content doubling up each time it is opened

I'm using Tooltipster to show a list of items that the user can click so as to enter the item into a textarea. When a tooltip is created, I get its list of items with selectors = $("ul.alternates > li");
However, each time a tooltip is opened the item clicked will be inserted a corresponding number of times; for example if I've opened a tooltip 5 times then the item clicked will be inserted 5 times. I've tried deleting the variable's value after a tooltip is closed with functionAfter: function() {selectors = null;} but that had no effect.
I have a Codepen of the error here that should make it clearer.
// set list to be tooltipstered
$(".commands > li").tooltipster({
interactive: true,
theme: "tooltipster-light",
functionInit: function(instance, helper) {
var content = $(helper.origin).find(".tooltip_content").detach();
instance.content(content);
},
functionReady: function() {
selectors = $("ul.alternates > li");
$(selectors).click(function() {
var sampleData = $(this).text();
insertText(sampleData);
});
},
// this doesn't work
functionAfter: function() {
selectors = null;
}
});
// Begin inputting of clicked text into editor
function insertText(data) {
var cm = $(".CodeMirror")[0].CodeMirror;
var doc = cm.getDoc();
var cursor = doc.getCursor(); // gets the line number in the cursor position
var line = doc.getLine(cursor.line); // get the line contents
var pos = {
line: cursor.line
};
if (line.length === 0) {
// check if the line is empty
// add the data
doc.replaceRange(data, pos);
} else {
// add a new line and the data
doc.replaceRange("\n" + data, pos);
}
}
var code = $(".codemirror-area")[0];
var editor = CodeMirror.fromTextArea(code, {
mode: "simplemode",
lineNumbers: true,
theme: "material",
scrollbarStyle: "simple",
extraKeys: { "Ctrl-Space": "autocomplete" }
});
body {
margin: 1em auto;
font-size: 16px;
}
.commands {
display: inline-block;
}
.tooltip {
position: relative;
opacity: 1;
color: inherit;
}
.alternates {
display: inline;
margin: 5px 10px;
padding-left: 0;
}
.tooltipster-content .alternates {
li {
list-style: none;
pointer-events: all;
padding: 15px 0;
cursor: pointer;
color: #333;
border-bottom: 1px solid #d3d3d3;
span {
font-weight: 600;
}
&:last-of-type {
border-bottom: none;
}
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.2/theme/material.min.css" rel="stylesheet"/>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/235651/jquery-3.2.1.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/235651/tooltipster.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.2/codemirror.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.2/addon/mode/simple.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.2/addon/hint/show-hint.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.2/addon/scroll/simplescrollbars.js"></script>
<div class="container">
<div class="row">
<div class="col-md-6">
<ul class="commands">
<li><span class="command">Hover for my list</span><div class="tooltip_content">
<ul class="alternates">
<li>Lorep item</li>
<li>Ipsum item</li>
<li>Dollar item</li>
</ul>
</li>
</div>
</ul>
</div>
<div class="col-md-6">
<textarea class="codemirror-area"></textarea>
</div>
</div>
</div>
Tooltipster's functionReady fires every time the tooltip is added to the DOM, which means every time a user hovers over the list, you are binding the event again.
Here are two ways to prevent this from happening:
Attach a click handler to anything that exists in the DOM before the tooltip is displayed. (Put it outside of tooltipspter(). No need to use functionReady.)
Example:
$(document).on('click','ul.alternates li', function(){
var sampleText = $(this).text();
insertText(sampleText);
})
Here's a Codepen.
Unbind and bind the event each time functionReady is triggered.
Example:
functionReady: function() {
selectors = $("ul.alternates > li");
$(selectors).off('click').on('click', function() {
var sampleData = $(this).text();
insertText(sampleData);
});
}
Here's a Codpen.
You are binding new clicks every time.
I would suggest different code style but in that format you can just add before the click event
$(selectors).unbind('click');
Then do the click again..

How to change the background color of a screen area on hover

I am making a website for a school project, wherein I have left and right drawers. The drawers are hidden and show only when onclick pageX < 100 (left drawer) and pageX > 1200 (right drawer). As the drawers show only onclick(), I want that area to get highlighted in some way (preferably color-change) so that the user knows there is something there. How do I do this?
HTML:
<div id="pgcontainer">
<header>
<div id="navbar">
<div id="rightdrawer">
<ul>
<li>Register</li>
<li>Archives</li>
<li>Contact Us</li>
<li>Our sponsors</li>
</ul>
</div>
</div>
</header>
</div>
JavaScript:
$(function() {
var menuwidth = 240; // pixel value for sliding menu width
var menuspeed = 400; // milliseconds for sliding menu animation time
var $bdy = $('body');
var $container = $('#pgcontainer');
var $hamburger = $('#hamburgermenu');
var $rightmenu = $('#rightdrawer');
var negwidth = "-"+menuwidth+"px";
var poswidth = menuwidth+"px";
$('#pgcontainer').on('click',function(e) {
if(e.pageX < 130) {
if($bdy.hasClass('openmenu')) {
jsAnimateMenuLeft('close');
} else {
jsAnimateMenuLeft('open');
}
}
});
$('.overlay').on('click', function(e) {
if($bdy.hasClass('openmenu')) {
jsAnimateMenuLeft('close');
}
else if($bdy.hasClass('openmenur')) {
jsAnimateMenuRight('close');
}
});
$('a[href$="#"]').on('click', function(e) {
e.preventDefault();
});
function jsAnimateMenuLeft(tog) {
if(tog == 'open') {
$bdy.addClass('openmenu');
$container.animate({marginRight: negwidth, marginLeft: poswidth}, menuspeed);
$hamburger.animate({width: poswidth}, menuspeed);
$('.overlay').animate({left: poswidth}, menuspeed);
}
if(tog == 'close') {
$bdy.removeClass('openmenu');
$container.animate({marginRight: "0", marginLeft: "0"}, menuspeed);
$hamburger.animate({width: "0"}, menuspeed);
$('.overlay').animate({left: "0"}, menuspeed);
}
}
});
I think that the optimal solution here is to add two more elements, position them fixed and add some nice hover styles.
Note that since .leftdrawer-hover and .rightdrawer-hover are children on #pgcontainer clicking on them would act exactly as you need, because click events will bubble to #pgcontainer where you will detect them and show/hide corresponding drawer.
#pgcontainer .leftdrawer-hover,
#pgcontainer .rightdrawer-hover {
content: '';
position: fixed;
top: 0;
bottom: 0;
width: 130px;
display: block;
background: rgba(200, 200, 200, .4);
}
#pgcontainer .rightdrawer-hover {
right: 0;
}
#pgcontainer .leftdrawer-hover:hover,
#pgcontainer .rightdrawer-hover:hover {
background: rgba(0, 0, 0, .7);
cursor: pointer;
}
<div id="pgcontainer">
<div class="leftdrawer-hover"></div>
<div class="rightdrawer-hover"></div>
<!-- other tags -->
</div>

JQuery Accordion - Last Cell Flip Sides

I really didn't know how to come up with a descriptive title for this. Pretty much what I'm trying to do is make this accordion list item jump to the other side of the page when clicked. Currently the accordion is opening from left to right - but the last cell doesn't jump right it instead stays in place. How can I make that last cell jump to the right instead of staying in place.
The point of this is to put a picture in the tabs and have them come together at the beginning and end of browsing links.
JSFiddle Example - click the last cell
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Accordion</title>
<link rel="stylesheet" type="text/css" href="redo.css" />
</head>
<body>
<div id="hc1" class="haccordion">
<ul>
<li>
<div class="hpanel">
<div class="preview" id="p1"></div>
<div class="contentContainer">
<div class="content">
</div>
</div>
</div>
</li>
<li>
<div class="hpanel">
<div class="preview" id="p2"></div>
<div class="contentContainer">
</div>
</div>
</li>
<li>
<div class="hpanel">
<div class="preview" id="p3"></div>
<div class="contentContainer">
</div>
</div>
</li>
<li>
<div class="hpanel">
<div class="preview" id="p4"></div>
<div class="contentContainer">
asdf
</div>
</div>
</li>
<li>
<div class="hpanel">
<div class="preview" id="p5"></div>
<div class="contentContainer">
</div>
</div>
</li>
</ul>
</div>
<!-- Scripts -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript" src="accordion.js"></script>
<!-- End Scripts -->
</body>
CSS
*
{
margin:0px;
padding:0px
}
html, body
{
height:100%;
width: 100%;
}
#hc1, #hc1 ul, #hc1 li
{
height: 100%;
}
#hc1, #hc1 ul
{
width: 100%;
}
.preview
{
width: 50px;
float: left;
height: 100%;
background-color: #E48525
}
#p1{background-color: #231F20}
#p2{background-color: #4F4E4F}
#p3{background-color: #919191}
#p4{background-color: #C4C4C3}
#p5{background-color: #E8E8E8}
/*
#p1{background-color: red}
#p2{background-color: blue}
#p3{background-color: green}
#p4{background-color: black}
#p5{background-color: orange}
*/
.contentContainer
{
background-color: gray;
margin: 0 auto;
width: 100%;
height: 100%;
}
/* -- Start Accordion -- */
.haccordion{
padding: 0;
}
.haccordion ul{
margin: 0;
padding: 0;
list-style: none;
overflow: hidden; /*leave as is*/
}
.haccordion li{
margin: 0;
padding: 0;
display: block; /*leave as is*/
overflow: hidden; /*leave as is*/
float: left; /*leave as is*/
}
/* -- End Accordion -- */
Javascript
var haccordion={
//customize loading message if accordion markup is fetched via Ajax:
ajaxloadingmsg: '<div style="margin: 1em; font-weight: bold"><img src="ajaxloadr.gif" style="vertical-align: middle" /></div>',
accordioninfo: {}, //class that holds config information of each haccordion instance
expandli:function(accordionid, targetli){
var config=haccordion.accordioninfo[accordionid]
var $targetli=(typeof targetli=="number")? config.$targetlis.eq(targetli) : (typeof targetli=="string")? jQuery('#'+targetli) : jQuery(targetli)
if (typeof config.$lastexpanded!="undefined") //targetli may be an index, ID string, or DOM reference to LI
{
config.$lastexpanded.stop().animate({width:config.paneldimensions.peekw}, config.speed); //contract last opened content
config.$lastexpanded.removeClass('active');
}
$targetli.stop().animate({width:$targetli.data('hpaneloffsetw')}, config.speed) //expand current content
config.$lastexpanded=$targetli
if($targetli.attr('class') != 'active')
$targetli.addClass('active');
},
urlparamselect:function(accordionid){
var result=window.location.search.match(new RegExp(accordionid+"=(\\d+)", "i")) //check for "?accordionid=index" in URL
if (result!=null)
result=parseInt(RegExp.$1)+"" //return value as string so 0 doesn't test for false
return result //returns null or index, where index is the desired selected hcontent index
},
getCookie:function(Name){
var re=new RegExp(Name+"=[^;]+", "i") //construct RE to search for target name/value pair
if (document.cookie.match(re)) //if cookie found
return document.cookie.match(re)[0].split("=")[1] //return its value
return null
},
setCookie:function(name, value){
document.cookie = name + "=" + value + "; path=/"
},
loadexternal:function($, config){ //function to fetch external page containing the entire accordion content markup
var $hcontainer=$('#'+config.ajaxsource.container).html(this.ajaxloadingmsg)
$.ajax({
url: config.ajaxsource.path, //path to external content
async: true,
error:function(ajaxrequest){
$hcontainer.html('Error fetching content.<br />Server Response: '+ajaxrequest.responseText)
},
success:function(content){
$hcontainer.html(content)
haccordion.init($, config)
}
})
},
init:function($, config){
haccordion.accordioninfo[config.accordionid]=config //cache config info for this accordion
var $targetlis=$('#'+config.accordionid).find('ul:eq(0) > li') //find top level LIs
config.$targetlis=$targetlis
config.selectedli=config.selectedli || [] //set default selectedli option
config.speed=config.speed || "normal" //set default speed
//KEY_CHANGE_BEGIN
var maxWidth = $targetlis.parent ().width ();
$targetlis.each ( function () { maxWidth -= $(this).outerWidth (true); } );
$targetlis.each(function(i){
var $target=$(this).data('pos', i) //give each li an index #
var lclMaxWidth = maxWidth + $target.find ('.hpanel:eq(0)').outerWidth (true);
$target.css ('width', config.paneldimensions.fullw);
//get offset width of each .hpanel DIV (config.dimensions.fullw + any DIV padding)
var hpaneloffsetw = $target.find ('.hpanel:eq(0)').outerWidth (true);
if (hpaneloffsetw > lclMaxWidth)
hpaneloffsetw = lclMaxWidth;
$target.data('hpaneloffsetw', hpaneloffsetw);
$target.css ('width', '');
//KEY_CHANGE_END
$target.click(function(){
haccordion.expandli(config.accordionid, this)
config.$lastexpanded=$(this);
})
if (config.collapsecurrent){ //if previous content should be contracted when expanding current
config.$lastexpanded.removeClass('active');
$target.click(function(){
$(this).stop().animate({width:config.paneldimensions.peekw}, config.speed); //contract previous content
})
}
}) //end $targetlis.each
var selectedli=haccordion.urlparamselect(config.accordionid) || ((config.selectedli[1] && haccordion.getCookie(config.accordionid))? parseInt(haccordion.getCookie(config.accordionid)) : config.selectedli[0])
selectedli=parseInt(selectedli)
if (selectedli>=0 && selectedli<config.$targetlis.length){ //if selectedli index is within range
config.$lastexpanded=$targetlis.eq(selectedli)
config.$lastexpanded.css('width', config.$lastexpanded.data('hpaneloffsetw')) //expand selected li
}
$(window).bind('unload', function(){ //clean up and persist on page unload
haccordion.uninit($, config)
}) //end window.onunload
},
uninit:function($, config){
var $targetlis=config.$targetlis
var expandedliindex=-1 //index of expanded content to remember (-1 indicates non)
$targetlis.each(function(){
var $target=$(this)
$target.unbind()
if ($target.width()==$target.data('hpaneloffsetw'))
expandedliindex=$target.data('pos')
})
if (config.selectedli[1]==true) //enable persistence?
haccordion.setCookie(config.accordionid, expandedliindex)
},
setup:function(config){
//Use JS to write out CSS that sets up initial dimensions of each LI, for JS enabled browsers only
document.write('<style type="text/css">\n')
document.write('#'+config.accordionid+' li{width: '+config.paneldimensions.peekw+';\nheight: '+config.paneldimensions.h+';\n}\n')
document.write('#'+config.accordionid+' li .hpanel{width: '+config.paneldimensions.fullw+';\nheight: '+config.paneldimensions.h+';\n}\n')
document.write('<\/style>')
jQuery(document).ready(function($){ //on Dom load
if (config.ajaxsource) //if config.ajaxsource option defined
haccordion.loadexternal($, config)
else
haccordion.init($, config)
}) //end DOM load
}
}
haccordion.setup({
accordionid: 'hc1', //main accordion div id
paneldimensions: {peekw:'50px', fullw:'100%', h:'100%'},
selectedli: [4, false], //[selectedli_index, persiststate_bool]
collapsecurrent: false //<- No comma following very last setting!
})
Here it is: tinker.io/f7fe4/12
This is the simplest change of all of the versions, requiring only floating the first preview to the right. Can be done programatically or with css (can be buggy in IE7+):
$('#hc1 li .preview').first().css('float','right');
or
#hc1 li:first-child .preview {
float:right;
}
--
Is this the kind of effect you're looking for?
https://tinker.io/f7fe4/8
Here's the same kind of affect, with a 'smoother' animation (it keeps the outer div still on the screen however)
https://tinker.io/f7fe4/9
And this is what I thought you were talking about at first
https://tinker.io/f7fe4/4 (this pops the left most cell over to the right and opens it, kind of like an infinite slider)

Categories