jQuery sortable obtain 2 elements being swapped - javascript

I cannot find out how to obtain destination element with jQuery UI sortable.
$("#pages").sortable({
opacity: 0.6,
update: function(event, ui) {
var first = ui.item; // First element to swap
var second = ???? // Second element to swap
swapOnServer(first, second);
}
});
All the options I've tried point to the element being dragged, but not the one it is swapped with: ui.item[0], event.srcElement, event.toElement.
Additionally, this points to the LIST (OL) element.
Saying second I mean following:
Original order is:
| 0 | 1 | 2 | 3 |
We drag element 1 and drop it in position 3. Which will end up with:
| 0 | 3 | 2 | 1 |
So the first element is 1 and the second is 3 (WRONG! See below).
UPDATE: I have realised that I got it wrong. The new order in this case will be.
| 0 | 2 | 3 | 1 |
As a result my question does not really makes sense. Thanks everybody for the help. I'll mark vote and mark an answer.
So the question is how to obtain the second element here?
THE CURRENT WORKAROUND (as there is no term as swapping in sortable) is below. It uses temporary array with orders.
var prevPagesOrder = [];
$("#pages").sortable({
start: function(event, ui) {
prevPagesOrder = $(this).sortable('toArray');
},
update: function(event, ui) {
var currentOrder = $(this).sortable('toArray');
var first = ui.item[0].id;
var second = currentOrder[prevPagesOrder.indexOf(first)];
swapOnServer(first, second);
}
});
Thanks,
Dmitriy.

You can use draggable and droppable instead of sortable to achieve swappable effect. In practise, this will look like this:
(function() {
var droppableParent;
$('ul .element').draggable({
revert: 'invalid',
revertDuration: 200,
start: function () {
droppableParent = $(this).parent();
$(this).addClass('being-dragged');
},
stop: function () {
$(this).removeClass('being-dragged');
}
});
$('ul li').droppable({
hoverClass: 'drop-hover',
drop: function (event, ui) {
var draggable = $(ui.draggable[0]),
draggableOffset = draggable.offset(),
container = $(event.target),
containerOffset = container.offset();
$('.element', event.target).appendTo(droppableParent).css({opacity: 0}).animate({opacity: 1}, 200);
draggable.appendTo(container).css({left: draggableOffset.left - containerOffset.left, top: draggableOffset.top - containerOffset.top}).animate({left: 0, top: 0}, 200);
}
});
} ());
Demo, http://jsfiddle.net/FZ42C/1/.

Try using the serialize function which gives you a hash of the list of items in order.
If you just need the item that the new item go dropped before you can do this:
$("#pages").sortable({
opacity: 0.6,
update: function(event, ui) {
var first = ui.item; // First element to swap
var second = ui.item.prev();
swapOnServer(first, second);
}
});
second will be null if its at the start of the list.

take a look at the "Swapable" jQuery plugin:
http://plugins.jquery.com/project/Swapable
It similar to "Sortable", but only two elements of the selected group are affected: dragged element and dropped one which are swapped. All other elements stay at their current positions. This plugin is built based on existing "Sortable" jQuery plugin and inherits all sortable options.

There's not really a "second" item per se. You have an item, and you are simply placing it in another location. The items around it adjust their positions accordingly. If you want to get an array of all the items, you can use the toArray method.

You can use http://plugins.jquery.com/project/Swapable
but this is not too good plugin

After reviewing the code I have made some behavior improvements:
<!doctype html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title></title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style id="compiled-css" type="text/css">
ul,
li{ margin: 0; padding: 0; }
ul { float:left; height: 120px; background: #CCC; overflow:auto; }
li { list-style: none; float: left; width: 100px; height: 100px; background: yellow; margin: 10px; position: relative; border: "1px solid yellow"}
li.drop-hover .element { opacity: .5; }
.element { position: absolute; width: 100px; height: 100px; background: #00f; color: #fff; text-align: center; line-height: 100px; z-index: 5; }
.element.being-dragged { background-color: #f00; z-index: 9999; }
</style>
<script src="//...jquery-3.6.0.min.js"></script>
<script src="//...jquery-ui.min.js"></script>
</head>
<body style="cursor: auto;">
<ul>
<li class="ui-droppable">
<div class="element a ui-draggable">a</div>
</li>
<li class="ui-droppable">
<div class="element b ui-draggable">b</div>
</li>
<li class="ui-droppable">
<div class="element c ui-draggable">c</div>
</li>
</ul>
<script type="text/javascript">
(function() {
var droppableParent;
var superDrop;
$('ul .element').draggable({
revert: true,
revertDuration: 200,
start: function (event, ui) {
droppableParent = $(this).parent();
ui.helper.css({zIndex: 9999, opacity: 0.5, border: "1px solid black"})
},
stop: function (event, ui) {
ui.helper.css({zIndex: 1, opacity: 1, border: "1px solid blue"})
}
});
$('ul li').droppable({
over: function( event, ui ) {
var draggable_clase = ui.draggable.attr("class");
var clase_actual = $(event.target).find(" > .element").attr("class")
var droppable = $(this).find(" > .element");
superDrop = $(this).find(" > .element")
if(draggable_clase != clase_actual)
droppable.css({border: "3px solid red", backgroundColor : "yellow"});
console.log(draggable_clase + "\n" + clase_actual)
},
out: function( event, ui ) {
var draggable_clase = ui.draggable.attr("class");
var clase_actual = $(event.target).find(" > .element").attr("class")
var droppable = $(this).find(" > .element");
if(draggable_clase != clase_actual)
droppable.css({border: "0px solid red", backgroundColor : "blue"});
var droppable = $(this).find(" > .element");
//droppable.css({border: "0px", backgroundColor : "#00f"});
},
drop: function (event, ui) {
var draggable = $(ui.draggable[0]),
draggableOffset = draggable.offset(),
container = $(event.target),
containerOffset = container.offset();
$(this).find(" > .element").appendTo(droppableParent);
//$(this).find(" > .element").css({zIndex: 1, opacity: 0.5, border: "1px solid black"})
//$(this).find(" > .element").css({opacity: 0.35}).animate({opacity: 1}, 200);
superDrop.css({border: "0px solid orange", backgroundColor : "blue"});
draggable.appendTo(container).css({
left: draggableOffset.left - containerOffset.left,
top: draggableOffset.top - containerOffset.top})
.animate({left: 0, top: 0}, 200);
}
});
} ());
</script>

Related

How to blur the whole body except a list item?

I wanted to create an effect where the whole body gets blurred or dimmed and only a particular list item appears clear. However when I set the z-index to the list item, it doesn't work. And when I set the z-index of the whole un-ordered list, it works but the all the list items appear clear (which I don't want).
Let me show you my html code:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ashish Toppo</title>
<link href="https://fonts.googleapis.com/css?family=Oxanium&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body >
<!-- the html for the top bar starts here -->
<div class="top_bar" id="topBar">
<div class="logo_name" id="logoName">Ashish Toppo</div>
<ul class="menu">
<li class="menu_items currently_active_menuItem" id="home">home</li>
<li class="menu_items" id="about">about</li>
<li class="menu_items" id="education">education</li>
</ul>
</div>
<!-- the html for the top bar ends here -->
<!-- the html for the intro starts here -->
<div class="intro" id="intro">
<div class="profile_pic" id="profilePic">
<img id="profileImg" src="images/ashish-toppo-green.jpg" width="100%" height="100%" alt="a picture of mine">
</div>
<div class="intro_box" id="introBox">
<!-- some introduction text here -->
<center id="aboutPointer">To know more about me, go to the about section!</center>
</div>
</div>
<!-- the html for the intro ends here -->
<script src="js/uiversal.js"></script>
<script src="js/index.js"></script>
</body>
</html>
Now, the Universal javaScript file:
/* this is a reusable js file universal to all web pages */
/* Ashish Toppo */
"use strict";
function get(id_or_class){
var obj = {
element: ( document.getElementById(id_or_class) ) ? document.getElementById(id_or_class) :
( document.getElementsByClassName(id_or_class) ) ? document.getElementsByClassName(id_or_class) :
( document.querySelector(id_or_class) ) ? document.querySelector(id_or_class) :
console.error("The provided HTML element could not be found"),
html: () => { return obj.element; },
changeText: (text) => { obj.html().innerHTML = text; },
appendText: (text) => {
let appendOn = obj.html().innerHTML;
obj.html().innerHTML = appendOn + text;
},
previousDisplayMode: "block",
hide: () => {
obj.previousDisplayMode = obj.html().style.display;
obj.html().style.display = "none";
},
show: () => {
obj.html().style.display = obj.previousDisplayMode;
},
on: (event, callBack) => {
obj.html().addEventListener(event, callBack);
},
previousZIndex: 1,
focusOn: () => {
let blur = document.createElement("div");
blur.className = "theDivThatBlurs";
blur.style.width ="100vw";
blur.style.height ="100vh";
blur.style.display ="block";
blur.style.position ="fixed";
blur.style.top ="0";
blur.style.left ="0";
blur.style.zIndex ="9";
blur.style.backgroundColor ="rgba(0, 0, 0, 0.9)";
blur.innerHTML = "";
document.body.appendChild(blur);
obj.html().style.zIndex = "100";
}
}
return obj;
}
and the index.js file was as followed:
/* my css wasn't working as i wanted, so i had to fix it using js */
"use strict";
(function(d){
const active = d.getElementsByClassName("currently_active_menuItem");
active[0].style.textDecoration = "none";
})(document);
var about = get("about");
var aboutPointer = get("aboutPointer");
aboutPointer.on("click", function(){
console.log("the about pointer has been clicked");
focus(about);
});
function focus(theElement){
console.log("the focus is working");
theElement.focusOn();
}
You can use the box-shadow property to achieve the dimming effect. Quick and easy :)
Just toggle a class programmatically and it should work for any element you have.
Code
function focusAndDim() {
document.getElementById("maindiv").classList.toggle("visible");
// if you want to get more fancy ;)
document.getElementsByTagName("body")[0].classList.toggle("blur");
}
.visible {
box-shadow: 0 0 0 10000px #ccc;
/* this code below make everything else hidden */
/* box-shadow: 0 0 0 10000px #fff; */
position: relative;
}
.btn {
height: 20px;
line-height: 1.4;
border: 2px solid #999;
padding: 12px 24px;
margin-bottom: 10px;
border-radius: 2px;
cursor: pointer;
}
body {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100vh;
}
body.blur div {
filter: blur(2px);
}
body.blur div.visible {
filter: blur(0);
}
<div class="btn" onclick="focusAndDim()" id="maindiv">Click Me</div>
<div>Other elements</div>

jQuery Draggable - Create draggable and start dragging on html5 native Drag And Drop api event

Okay, basically what I want to try to achieve, is when a dragover event fires from HTML5 Drag And Drop API, I wish to create a jQuery draggable object, and start following the mouse around, while the dragend event fires from the HTML5 Drag And Drop API.
The reason I want to do this, is the following:
I have an application which uses a plugin, that has a functionality, which is dependent on the jQuery.ui draggable to function (it is the FullCalendar Scheduler plugin, version 3)
I want to achieve a new functionality in the application, with which the client can drag something from browser window A, and drop it in the above mentioned plugin in browser window B.
As the above mentioned plugin is not working with the native HTML5 Drag and Drop API, and the jQuery.ui draggable is not capable of dragging elements from one browser window to the other, I think my only option is to mix these two plugins.
My proposed solution to this problem was, using the native HTML5 Drag and Drop API, and when the dragged element reaches over a dropzone, creating a new draggable element in browser window B, and simulating a mousedown event on it, so it starts following the cursor. When the dragend event would fire, I planned to plain and simply fire the mouseup event on the draggable element also, and from here on the scheduler plugin can do it's magic.
To try to test this out, with a single browser window at first, I've tried to achieve the first part of my above solution, ie: when the dragover fires, create the jQuery.ui draggable and simulate a mousedown on it, then it should start following the mouse. I can't achieve this behaviour.
I made a fiddle, where you can see what I tried so far (I am not posting the whole code here, as it is rather long): JSFiddle
Basically, the error I am getting at the Fiddle, with both options that I tried, is a type.indexOf is not a function error.
I also asked and received some help on the following question, from where the proposed solution works fine when starting the drag operation with a click event, but it isn't working with any other event type. I pressume, I can simulate a mousedown.draggable event, only from a MouseEvent, and the dragend event is not a MouseEvent.
Long story short, I would need help in obtaining the result I am looking for, at least for the first part of my proposed solution!
There does not appear to be a good answer for this. First, not all browsers support the same DnD terminology or functionality. Such as FireFox fires a dragenter event on drop and Chrome does not seem to detect a drop event when the object is from another window.
Here is my testing so far. To use, copy the content into a Text file and save as HTM or HTML. Then Open the file locally in your browser. Open another Window and open the second HTM. now you have two windows you can drag to and from.
wina-1.htm
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Window A</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<style>
.items {
position: relative;
}
.items > div {
margin-right: 5px;
width: 150px;
height: 150px;
padding: 0.5em;
border-radius: 6px;
display: inline-block;
}
#log {
width: 100%;
height: 5em;
overflow-y: auto;
}
[draggable].idle {
background-color: rgba(255,0,0,0.75);
}
[draggable].selected {
background-color: rgba(255,0,0,0.95);
}
</style>
</head>
<body>
<pre id="log"></pre>
<div class="items ui-widget">
<div id="draggable" class="ui-widget-content idle" draggable="true">
<p>Drag me around</p>
</div>
<div id="static" class="ui-widget-content">
<p>I can't be moved</p>
</div>
</div>
<script>
var srcEl;
function log(s){
var now = new Date();
var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds()
+ "." + now.getMilliseconds();
var l = document.getElementById("log");
l.append(t + ": " + s + "\r\n");
l.scrollTop = l.scrollHeight;
}
function dragStart(e){
log("Drag Start: " + e.target.nodeName + "#" + e.target.id);
srcEl = e.target;
if(e.dataTransfer == undefined){} else {
e.dataTransfer.effectAllowed = "copyMove";
log("Event dataTransfer.effectAllowed: " +
e.dataTransfer.effectAllowed);
log("Source Element: " + srcEl.nodeName + "#" + srcEl.id);
}
this.classList.add("selected");
}
function dragOver(e){
e.preventDefault();
log("Drag Over: " + e.target.nodeName + (e.target.id != "" ? "#" +
e.target.id : ""));
return false;
}
function dragLeave(e){
log("Drag Leave: " + e.target.nodeName + (e.target.id != "" ? "#" +
e.target.id : ""));
}
function dragStop(e){
log("Drag End: " + e.target.nodeName + "#" + e.target.id);
this.classList.remove("selected");
}
log("Init");
var item = document.getElementById("draggable");
item.addEventListener('dragstart', dragStart, false);
item.addEventListener('dragover', dragOver, false);
item.addEventListener('dragleave', dragLeave, false);
window.addEventListener('dragleave', dragLeave, false);
var items = document.querySelectorAll('.items > div');
[].forEach.call(items, function(el) {
el.addEventListener('dragover', dragOver, false);
});
</script>
</body>
</html>
As you can see, this is using raw JavaScript. I was tinkering with jQuery UI, and I kept the stylesheet just for easy theming. We have a section to print out log details, a draggable, and a static item.
winb-1.htm
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Window B</title>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
.drag-item {
width: 100px;
height: 100px;
background-color: red;
}
body {
position: relative;
}
div.drag-helper {
width: 100px;
height: 100px;
background-color: red;
z-index: 1002;
position: relative;
}
#log {
width: 100%;
height: 5em;
line-height: 1em;
font-size: 1em;
overflow-y: auto;
}
#dropzone {
background-color: green;
width: 95%;
height: 340px;
}
</style>
</head>
<body>
<pre id="log"></pre>
<div id="dropzone"></div>
<script>
jQuery(function($) {
function log(s){
var now = new Date();
var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds
() + "." + now.getMilliseconds();
$("#log").append(t + ": " + s + "\r\n").scrollTop($("#log").prop
("scrollHeight"));
}
function dragEnter(e){
e.preventDefault();
log("Drag Enter triggered: " + $(e.target).prop("nodeName") +
($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
}
function dragOver(e){
log("Drag Over triggered: " + $(e.target).prop("nodeName") +
($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
e.dataTransfer.dropEffect = 'move';
e.preventDefault();
}
function handleDrop(e){
if (e.stopPropagation) {
e.stopPropagation();
}
log("Drop Triggered: " + $(e.target).attr("id"));
return false;
}
function dragEnd(e){
log("Drag End Triggered: " + $(e.target).prop("nodeName") +
($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
}
log("Init");
$("#dropzone").on({
dragenter: dragEnter,
dragover: dragOver,
drop: handleDrop,
mouseup: handleDrop,
dragend: dragEnd
});
$(window).on({
dragenter: dragEnter,
dragover: dragOver,
drop: handleDrop,
dragend: dragEnd
});
});
</script>
</body>
</html>
Window B uses jQuery as the intention was to convert the element into a jQuery UI Draggable.
First thing to know, there is no way to do in transit. Since the Source element is not a part of the target DOM, it cannot be done. It can be added and initialized as a Draggable in the drop event. Essentially what will happen is a new element will be created at that time assigned all the data.
Second, data transfer is unreliable and I would avoid DataTransfer as your data container. I would advise using localStorage. This is similar to a cookie and is a lot more reliable.
For example, I created the following Data object:
{
id,
type,
attr: {
id,
class,
width,
height
},
content
}
Here are some example functions:
function collectData(obj){
return {
id: obj.attr("id"),
type: obj.prop("nodeName"),
attr: {
id: obj.attr("id"),
class: obj.attr("class"),
width: obj.width(),
height: obj.height()
},
content: obj.text().trim()
};
}
function saveData(k, d){
localStorage.setItem(k, JSON.stringify(d));
}
function getData(k){
return JSON.parse(localStorage.getItem(k));
}
function makeEl(d, pObj){
return $("<" + d.type +">", d.attr).html("<p>" + d.content + "</p>").appendTo(pObj);
}
$("#draggable").on('dragstart', function(e){
saveData("drag-data", collectData($(this)));
});
$("#dropzone").on('drop', function(e){
var item = makeEl(getData('drag-data'), $(this));
item.addClass("clone").position({
my: "center",
of: e
}).draggable();
});
In theory, this should all work. In practice, I have hit a ton of roadblocks. I would suggest something like a click-to-copy type of action. Where the User clicks an item in Window A (selecting it) and then clicks where they want it to be in Window B. Again using localStorage, the item could be cloned into the new location.
wina-3.htm
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jQuery UI Draggable - Default functionality</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<style>
.items {
position: relative;
}
.items > div {
margin-right: 5px;
width: 150px;
height: 150px;
padding: 0.5em;
border-radius: 6px;
display: inline-block;
}
#log {
width: 100%;
height: 5em;
overflow-y: auto;
}
[draggable].idle {
background-color: rgba(255,0,0,0.5);
}
[draggable].selected {
background-color: rgba(255,0,0,0.95);
}
</style>
</head>
<body>
<pre id="log"></pre>
<div class="items ui-widget">
<div id="draggable" class="ui-widget-content idle" draggable="true">
<p>Click on me</p>
</div>
<div id="static" class="ui-widget-content">
<p>I can't be moved</p>
</div>
</div>
<script>
var intv;
function log(s){
var now = new Date();
var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + "." + now.getMilliseconds();
var l = document.getElementById("log");
l.append(t + ": " + s + "\r\n");
l.scrollTop = l.scrollHeight;
}
function collectData(el){
return {
id: el.id,
type: el.nodeName,
attr: {
id: el.id,
class: el.className,
width: el.width,
height: el.height
},
content: el.innerText
};
}
function saveData(k, v){
localStorage.setItem(k, JSON.stringify(v));
}
function getData(k){
return JSON.parse(localStorage.getItem(k));
}
function clearData(k){
localStorage.setItem(k, null);
}
function selElem(e){
var trg = e.target.nodeName + (e.target.id != "" ? "#" + e.target.id : "");
if(e.target.classList.contains("selected")){
log("Deselect element: " + trg);
e.target.classList.remove("selected");
} else {
log("Element Selected: " + trg);
e.target.classList.add("selected");
saveData("select-data", collectData(e.target));
}
intv = setInterval(function(){
if(getData("select-data") == null){
document.getElementsByClassName("selected")[0].classList.remove("selected");
log("Unselected");
clearInterval(intv);
}
}, 1000);
}
log("Init");
var item = document.getElementById("draggable");
item.addEventListener('click', selElem);
</script>
</body>
</html>
winb-3.htm
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Window B</title>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
.drag-item {
width: 100px;
height: 100px;
background-color: red;
}
body {
position: relative;
}
#log {
width: 100%;
height: 5em;
line-height: 1em;
font-size: 1em;
overflow-y: auto;
}
#dropzone {
background-color: green;
width: 95%;
height: 340px;
position: relative;
}
.cloned {
position: absolute;
width: 150px;
height: 150px;
padding: 0.5em;
border-radius: 6px;
display: inline-block;
background-color: rgba(255,0,0,0.75);
}
</style>
</head>
<body>
<pre id="log"></pre>
<div id="dropzone"></div>
<script>
jQuery(function($) {
function log(s){
var now = new Date();
var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds
() + "." + now.getMilliseconds();
$("#log").append(t + ": " + s + "\r\n").scrollTop($("#log").prop
("scrollHeight"));
}
function getData(k){
console.log("Getting Data: '" + k + "'", localStorage.getItem(k));
return JSON.parse(localStorage.getItem(k));
}
function clearData(k){
log("Clear Data");
localStorage.setItem(k, null);
}
function makeEl(dObj, pObj){
console.log(dObj, pObj);
return $("<" + dObj.type + ">", dObj.attr).html("<p>" + dObj.content +
"</p>").appendTo(pObj);
}
function handleDrop(e){
if (e.stopPropagation) {
e.stopPropagation();
}
var trg = $(e.target);
log("Drop Triggered: " + trg.prop("nodeName") + "#" + trg.attr("id"));
var d, item;
if(e.target.id == "dropzone" && (e.type == "click" || e.type ==
"mouseup")){
log("Click Detected - Collecting Data");
d = getData("select-data");
console.log("Data", d);
d.attr.id = "clone-" + ($("#dropzone .cloned").length + 1);
log("Making Element: " + d.type + "#" + d.attr.id);
item = makeEl(d, trg);
item.removeClass("selected").addClass("cloned").position({
my: "center",
of: e
}).draggable();
clearData("select-data");
return true;
}
return false;
}
log("Init");
$("#dropzone").on({
mouseup: handleDrop,
click: handleDrop
});
});
</script>
</body>
</html>
I know this is not the answer you're looking for, and for that you need to try to ask the real question. You seem to keep asking around the question.
Hope this helps.

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..

jQuery animation without queuing

I am trying to figure out how .stop() and clearQueue() work.
Here is my sample code in jsfiddle: http://jsfiddle.net/PsTQv/1/
You will see the animation is queuing if hove over several blocks.
To work around with this, I tried to use stop() and clearQueue.Simple add stop after hide() or show() or both won't work.The behaviors like:
1. .stop().hide() : text disappears at last;
2. .stop.show(): text is alway there, won't be hidden any more
3. add both: Same as only add to show()
4. add .clearQueue().stop() in the beginning: text disappears at last, like .stop().hide()
I want to understand what the differences between clearQueue and stop to explain the behaviors above.Also I want to figure out how to achieve the animation without queueing in this example, that is, hover over the block and the text shows up in the slide effect.
Thanks
You need to clear the animation queue in the callback function that executes when the slide animation is done:
$('.block').hover(function(){
$('section').hide('fast');
//$('section').stop().show('slide',{direction:'left'},1000);
$('section').show('slide',{direction:'left'},1000,function(){$(this).clearQueue()});
},function(){});
jsFiddle
var inAnimation = new Array();
$("div").hover(function(){
if (!inAnimation[$("div").index(this)] ) {
$(this).animate({ width: "200px" });
}
}, function() {
inAnimation[$("div").index(this)] = true;
$(this).animate({ width: "100px" }, "normal", "linear", function() {
inAnimation[$("div").index(this)] = false;
})
});
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>clearQueue demo</title>
<style>
div {
margin: 3px;
width: 40px;
height: 40px;
position: absolute;
left: 0px;
top: 30px;
background: green;
display: none;
}
div.newcolor {
background: blue;
}
</style>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<button id="start">Start</button>
<button id="stop">Stop</button>
<div></div>
<script>
$( "#start" ).click(function() {
var myDiv = $( "div" );
myDiv.show( "slow" );
myDiv.animate({
left:"+=200"
}, 5000 );
myDiv.queue(function() {
var that = $( this );
that.addClass( "newcolor" );
that.dequeue();
});
myDiv.animate({
left:"-=200"
}, 1500 );
myDiv.queue(function() {
var that = $( this );
that.removeClass( "newcolor" );
that.dequeue();
});
myDiv.slideUp();
});
$( "#stop" ).click(function() {
var myDiv = $( "div" );
myDiv.clearQueue();
myDiv.stop();
});
</script>
</body>
</html>

Dynamically assigning border radius to the li first-child in Javascript

I am trying to create a navigation bar with rounded corners for the first and last children (using an unordered list). I want to use the onclick javascript function and dynamically assign rounded corners within javascript. Here is my code which I tried. Can anyone please point me to the reason and/or suggest a solution or resource? Thanks a lot in advance.
HTML:
<nav>
<ul id="navBar">
<li><div class="menu" onClick="select(this)">View</div></li>
<li><div class="menu" onClick="select(this)">Duplicate</div></li>
<li><div class="menu" onClick="select(this)">Edit</div></li>
<li><div class="menu" onClick="select(this)">Delete</div></li>
</ul>
</nav>
Javascript:
document.getElementById('ul#navBar li:first-child').style.MozBorderRadiusTopleft = '13px';
document.getElementById('ul#navBar li:first-child').style.MozBorderRadiusBottomleft = '13px';
document.getElementById('ul#navBar li:last-child').style.MozBorderRadiusTopRight = '13px';
document.getElementById('ul#navBar li:last-child').style.MozBorderRadiusBottomRight = '13px';
CSS for your stylesheet:
.is-rounded li:first-child {
-moz-border-radius: 13px 0 0 13px;
-webkit-border-radius: 13px 0 0 13px;
border-radius: 13px 0 0 13px;
}
.is-rounded li:last-child {
-moz-border-radius: 0 13px 13px 0;
-webkit-border-radius: 0 13px 13px 0;
border-radius: 0 13px 13px 0;
}
Javascript:
function makeRounded(e) {
document.querySelector('#navBar').className += 'is-rounded';
}
document.querySelector('#el').addEventListener('click', makeRounded, false);
This assumes than an element with the id of "el" is the element that, when clicked, triggers the whole thing.
See example: http://jsfiddle.net/cgrFe/3/
You seem to know very little about this. A few books to get you started: http://domscripting.com/book/ and http://domenlightenment.com/ . The last of the two is fairly new, but must be good.
getElementById does not take a selector as its argument. Only a valid id of the element is required for its usage. You're probably thinking about jQuery:
$('ul#navBar li:first-child').css({
MozBorderRadiusTopleft: '13px'
});
$('navBar li:first-child').css({
MozBorderRadiusBottomleft: '13px'
});
$('ul#navBar li:last-child').css({
MozBorderRadiusTopRight: '13px'
});
$('navBar li:last-child').css({
MozBorderRadiusBottomRight: '13px'
});
I would make things really simple. Start by defining two css classes for rounded corners:
.round-top {
MozBorderRadiusTopleft:13px;
MozBorderRadiusTopRight:13px;
}
.round-bottom {
MozBorderRadiusBottomleft:13px;
MozBorderRadiusBottomRight:13px;
}
Then you can simply add/remove the class from the elements that you are interested in.
In javascript:
var container = document.getElementById(navBar);
container.firstChild.setAttribute("class", "round-top");
container.lastChild.serAttribute("class", "round-bottom");
Jquery:
$('#navBar li:first-child').addClass('round-top');
$('#navBar li:last-child').addClass('round-bottom');
A full event listener would be something like:
function addBorders(){
var container = document.getElementById(navBar);
container.firstChild.setAttribute("class", "round-top");
container.lastChild.serAttribute("class", "round-bottom");
}
//target is the element you want to trigger the event
target.addEventListener("click", addBorders, false);
If you can use jquery, you can do it in next way
intead "border", "3px double red" you should use your styles
<script type="text/javascript" language="javascript">
var Menu = {
rootEl: null,
Init: function (idRootEl) {
this.rootEl = $(idRootEl);
this.initItems();
this.setFirstElSeleceted();
},
setFirstElSeleceted: function () {
$($(this.rootEl).find('li')[0]).css("border", "3px double red");
},
initItems: function () {
var self = this;
$(self.rootEl).find('li').each(function (indx) {
$(this).click(function () {
self.removeSelection();
self.setSelected(this);
});
});
},
setSelected: function (el) {
$(el).css("border", "3px double red");
},
removeSelection: function () {
var self = this;
$(self.rootEl).find('li').each(function (el) {
$(this).css("border", "");
});
}
};
$(function() {
Menu.Init('ul#navBar');
});
</script>

Categories