I wrote some kind of that: http://jsfiddle.net/py3DE/203/ but when element is dragged into proper container, we can override it by dragging other div into its area. Could you tel me how i can block dragged elements, and if someone will try to override any element, div returns back into area with undragged divs?
if (!ui.draggable.closest('.empty').length) item = item.draggable()'
There is a simple way to do this. Basically, we'll remove the class empty and use the disable method.
Working Example: http://jsfiddle.net/Twisty/5rdxmp4p/
Minor CSS Change
.filled .item .closer {
display: block;
}
Drop Function
drop: function(ev, ui) {
if ($(this).hasClass("empty")) {
$(this).removeClass("empty").addClass("filled");
$(this).droppable("disable");
} else {
return false;
}
var item = ui.draggable;
if (!ui.draggable.closest('.empty').length) item = item.draggable(); // if item was dragged from the source list - clone it
this.innerHTML = ''; // clean the placeholder
item.css({
top: 0,
left: 0
}).appendTo(this); // append item to placeholder
}
Swapping the class allows the X button to appear. We then run the disable method to ensure that this specific item will no longer accept a dragged item. If the user drags an item to this spot, it is then reverted.
Update
Using Sortable: http://jsfiddle.net/Twisty/5rdxmp4p/2/
HTML
<div id="dragItems" class="source">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<div id="sortItems" class="target">
</div>
CSS
body {
background: #fff;
}
.source,
.target {
margin: 20px;
min-height: 190px;
width: 400px;
border: 1px solid green;
}
.target {
border: 1px solid blue;
}
.item {
height: 20px;
margin: 5px;
padding: 5px;
border: 1px solid gray;
background-color: #cd8;
position: relative;
}
.closer {
float: right;
width: 20px;
height: 20px;
border: 0;
background-color: transparent;
}
.closer:hover {
background-color: transparent;
border: 0;
}
.empty {
height: 30px;
margin: 5px;
background: #eee;
border: 1px dashed #999;
}
.highlight {
border: 1px solid red;
background: #fff;
}
.highlight .item {
opacity: 0.3;
}
.ui-draggable-dragging {
z-index: 99;
opacity: 1 !important;
width: 378px;
}
jQuery
$(function() {
$("#sortItems").sortable({
axis: "y",
items: "> div",
placeholder: "empty",
dropOnEmpty: true,
stop: function(e, ui) {
var $it = ui.item;
if ($it.find(".closer").length == 0) {
var closeBtn = $("<span>", {
class: "closer"
});
$it.append(closeBtn);
closeBtn.button({
icon: "ui-icon-close",
label: "Close",
showLabel: false
}).click(function(ev) {
console.log("[INFO]: Closing ", $it);
$it.fadeTo(200, 0.0, function() {
$it.remove();
$("#sortItems").sortable("refresh");
});
});
}
}
});
$("#dragItems .item").draggable({
connectToSortable: "#sortItems",
revert: "invalid"
});
$("#sortItems").disableSelection();
});
Related
can someone please help me with this? The problem is explained in the video.
https://www.loom.com/share/dba701f22d1c42d38617d9b835d713e4
Here is what I am trying to do, on the input field we will add a youtube video URL and when clicked on the Generate Embed Code button it should output the embed code inside the text aria, do you know why its not working?
<div class="youtube-iframe-generator">
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed&display=swap" rel="stylesheet">
<script>
window.onload = setup;
function setup() {
document.getElementById("button").addEventListener("click", go);
document.getElementById("copy-button").addEventListener("click", copy);
}
function setStatus(status, error = false) {
var helpText = document.getElementById("help-text");
helpText.innerText = status;
if (error) {
helpText.style.color = "darkred";
} else {
helpText.style.color = "#227300";
}
}
function go() {
var videoUrl = document.getElementById("input").value;
var videoIdExtractor = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i;
var result = videoIdExtractor.exec(videoUrl);
var EmbedCode = document.getElementById("EmbedCode");
var copyButton = document.getElementById("copy-button");
if (!result) {
EmbedCode.innerHTML = "";
copyButton.disabled = true;
setStatus("Error: Invalid YouTube URL", true);
return;
}
var videoId = result[1];
var finalLink = "https://www.youtube.com/embed/" + videoId;
var imgLink = "https://img.youtube.com/vi/"+videoId+"/maxresdefault.jpg";
var iframe = '<iframe class="youtube-iframe" srcdoc="<style>*{padding:0;margin:0;overflow:hidden}html,body{height:100%}img{position:absolute;width:100%;top:0;bottom:0;margin:auto}span{position: absolute;content: '\f04b';top: 50%;left: 50%;background-color: #6c6cfe;width: 68px;height: 44px;margin-left: -34px;margin-top: -24px;font-size: 30px;color: #fff;text-align: center;line-height: 43px;z-index: 5;border-radius: 6px;}a{display:block;height:100%;}</style><img src="'+imgLink+'" alt="Video The Dark Knight Rises: What Went Wrong? – Wisecrack Edition"><span>▶</span>" frameborder="0" style="aspect-ratio: 16 / 9; width: 100%;" allow="accelerometer; autoplay; encrypted-media; gyroscope;" allowfullscreen></iframe>';
EmbedCode.disabled = false;
copyButton.disabled = false;
EmbedCode.value = iframe;
setStatus("Success! Click the 'Copy' button to copy the embed code.");
}
function copy() {
var EmbedCode = document.getElementById("EmbedCode");
EmbedCode.select();
document.execCommand("copy");
setStatus("Embed code copied to clipboard!");
}
}
</script>
<div class="title">Enter your Youtube URL: <span class="sub">(Scroll down for instructions)</span>
<style>
.youtube-iframe-generator {
font-family: 'Roboto Condensed', sans-serif;
width:96.9%;
}
.title {
font-size: 1.6em;
margin: 15px 0 10px;
}
.sub {
font-size: 0.5em;
color: #444;
vertical-align: middle;
}
#copy-button,
#button {
display: block;
font-family: 'Roboto Condensed', sans-serif;
font-size: 1.2em;
padding: 10px 18px;
margin: 15px auto;
border: none;
border-radius: 3px;
background: #598cff;
color: #ffffff;
cursor: pointer;
box-shadow: 0 2px 3px 0px #b5b5b5;
transition: background 200ms;
}
#copy-button:hover,
#button:hover {
background: #6b99ff;
}
#copy-button:active,
#button:active {
background: #598cff;
box-shadow: 0 2px 3px -1px #b5b5b5;
}
#input{
font-size: 1.1em;
display: block;
width: 100%;
padding: 10px;
border: 1px solid #b5b5b5;
border-radius: 3px;
}
#EmbedCode {
font-size: 1.1em;
display: block;
width: 100%;
padding: 10px;
border: 1px solid #b5b5b5;
border-radius: 3px;
resize: vertical;
overflow: auto;
}
#help-text {
font-weight: 600;
margin: 10px;
}
</style>
</div>
<input id="input" type="text" placeholder="https://www.youtube.com/watch?v=h4iRMtQBGAk">
<button id="button">Generate Embed Code</button>
<div class="title">Embed Code:</div>
<textarea id="EmbedCode" type="text" readonly="" disabled=""></textarea>
<button id="copy-button" disabled> <i class="fa-regular fa-clipboard"></i> Copy Embed Code</button>
<div id="help-text"></div>
</div>
How can I make a loop inside currentSlide(1) so I want everytime .each work to add currentSlide(1) then currentSlide(2) then currentSlide(3) and so on.
<div class="approach" id="app">
<style>
#media (max-width: 600px){
.btn {
padding: 2px 2px 2px 2px !important; */
}}
#myBtnContainer{
display: flex;
}
/* Create three equal columns that floats next to each other */
.column {
float: left;
display: none;
margin: 5px;
border: 1px solid #ccc;
float: left;
width: 282px;
}
.column:hover{
border: 1px solid #777;
}
/* Clear floats after rows */
.row:after {
content: "";
display: table;
clear: both;
}
/* Content */
.content {
width: 100%;
height: 400px;
overflow: hidden;
/*display: flex;*/
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
}
.content img{
object-fit: cover;
height: auto;
width: 100%;
position: absolute;
}
/* The "show" class is added to the filtered elements */
.show {
display: block;
}
/* Style the buttons */
.btn {
border: none;
outline: none;
padding: 10px 10px;
background-color: #333333;
cursor: pointer;
color: white;
}
.btn:hover {
background-color: #ddd;
}
.btn.active {
background-color: #666;
color: white;
}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: black;
}
/* Modal Content */
.modal-content {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 0;
width: 90%;
max-width: 500px;
}
/* The Close Button */
.close {
color: white !important;
position: absolute;
top: 170px;
right: 25px;
font-size: 35px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #999;
text-decoration: none;
cursor: pointer;
}
.mySlides {
display: none;
}
.cursor {
cursor: pointer
}
/* Next & previous buttons */
.prev,
.next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -50px;
color: white;
font-weight: bold;
font-size: 20px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
-webkit-user-select: none;
}
/* Position the "next button" to the right */
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
/* On hover, add a black background color with a little bit see-through */
.prev:hover,
.next:hover {
background-color: rgba(0, 0, 0, 0.8);
}
/* Number text (1/3 etc) */
.numbertext {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
}
img {
margin-bottom: -4px;
}
.caption-container {
text-align: center;
background-color: black;
padding: 2px 16px;
color: white;
}
.demo {
opacity: 0.6;
}
.active,
.demo:hover {
opacity: 1;
}
img.hover-shadow {
transition: 0.3s
}
.hover-shadow:hover {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)
}
.mySlides img{
padding-top: 70px;
background-color: black;
}
div.desc {
padding: 15px;
text-align: center;
}
#media (max-width:600px) {
.close {
color: white !important;
position: absolute;
top: 120px;
right: 25px;
font-size: 45px;
font-weight: bold;
z-index: 1000;
}
.column {
float: left;
display: none;
margin: 5px;
border: 1px solid #ccc;
float: left;
width: 83px;
}
}
#media (max-width: 320px){
.column {
float: left;
display: none;
margin: 5px;
border: 1px solid #ccc;
float: left;
width: 70px;
}
}
#media only screen and (min-width: 812px) {
.column {
float: left;
display: none;
margin: 5px;
border: 1px solid #ccc;
float: left;
width: 90px;
} }
</style>
<body>
<!-- MAIN (Center website) -->
<div class="container">
<h1>GALLERY</h1>
<div id="myBtnContainer">
<button class="btn" onclick="filterSelection('conversion')"> Conversion</button>
<button class="btn" onclick="filterSelection('fixed-hybrid')"> Fixed Hybrid</button>
<button class="btn" onclick="filterSelection('removal-bar-over-denture')"> Removable Bar Over Denture</button>
<button class="btn" onclick="filterSelection('surgical-guides')"> Surgical Guides</button>
<button class="btn active" onclick="filterSelection('all')"> Show all</button>
</div>
<br>
<!-- Portfolio Gallery Grid -->
<div class="row">
<% #galleries.each do |g| %>
<% [g.image1, g.image2, g.image3, g.image4, g.image5].compact.each_with_index do |image, index| %>
<div class="column <%= g.product_name %>">
<div class="content">
<%= image_tag image.url, style: "width:100%", onclick: "openModal();currentSlide(#{index + 1})" ,class: "hover-shadow cursor"%>
</div>
</div>
<% end %>
<% end %>
<!-- END GRID -->
</div>
<!-- END MAIN -->
</div>
<div id="myModal" class="modal">
<span class="close cursor" onclick="closeModal()">×</span>
<div class="modal-content">
<% #galleries.each do |g| %>
<% [g.image1, g.image2, g.image3, g.image4, g.image5].compact.each_with_index do |image, index| %>
<div class="mySlides">
<div class="numbertext">1 / 4</div>
<%= image_tag image.url, style: "width:100%" %>
</div>
<% end %>
<% end %>
<a class="prev" onclick="plusSlides(-1)">❮</a>
<a class="next" onclick="plusSlides(1)">❯</a>
<div class="caption-container">
<p id="caption">hr</p>
</div>
</div>
<script>
filterSelection("all")
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("column");
if (c == "all") c = "";
for (i = 0; i < x.length; i++) {
w3RemoveClass(x[i], "show");
if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
}
}
function w3AddClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1) {element.className += " " + arr2[i];}
}
}
function w3RemoveClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function(){
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
</script>
</div>
<script>
function openModal() {
document.getElementById('myModal').style.display = "block";
}
function closeModal() {
document.getElementById('myModal').style.display = "none";
}
var slideIndex = 1;
showSlides(slideIndex);
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("demo");
var captionText = document.getElementById("caption");
if (n > slides.length) {slideIndex = 1}
if (n < 1) {slideIndex = slides.length}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " active";
captionText.innerHTML = dots[slideIndex-1].alt;
}
</script>
Looks like you just need an index to interpolate into currentSlide.
Start by removing duplication and instead of checking every related image with <% if g.image1.present? %> put them into an array and iterate over it with:
<% [g.image1, g.image2, g.image3].compact.each_with_index do |image, index|
compact will make sure that only present images are there, so you don't have to use if present any more.
And then use onclick: "openModal();currentSlide(#{index + 1})" (as index starts at 0, and you seem to need it to start from 1)
Update:
<% #galleries.each do |g| %>
<% [g.image1, g.image2, g.image3, g.image4, g.image5].compact.each_with_index do |image, index| %>
<div class="column <%= g.product_name %>">
<div class="content">
<%= image_tag image.url,
alt: "conversion",
style: "width:100%",
onclick: "openModal();currentSlide(#{index + 1})",
class: "hover-shadow cursor" %>
</div>
</div>
I have this setup. JSFiddle link.
$('.item').draggable({
helper: 'clone',
connectToSortable: '.first-sortable, .second-sortable',
});
$('.first-sortable').sortable({
connectWith: '.second-sortable',
receive: function () {
$('.logger1').addClass('animated');
setTimeout(function () {
$('.logger1').removeClass('animated');
}, 300);
}
});
$('.second-sortable').sortable({
connectWith: '.first-sortable',
receive: function () {
$('.logger2').addClass('animated');
setTimeout(function () {
$('.logger2').removeClass('animated');
}, 300);
}
});
.item {
width: 50px;
height: 50px;
background: red;
display: inline-block;
margin-right: 5px;
}
.container {
position: relative;
margin-bottom: 20px;
}
.first-sortable {
height: 100px;
background: blue;
}
.second-sortable {
position: absolute;
left: 50%;
width: 200px;
transform: translateX(-50%);
top: 0;
bottom: 0;
background: rgba(3, 3, 3, .8);
z-index: 10;
padding-left: 20px;
}
.logger2,
.logger1 {
transition: all .3s color;
}
.animated {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div class="container">
<div class="first-sortable"></div>
<div class="second-sortable"></div>
</div>
<div class="item"></div>
<div class="logger1">Blue Sortable Receive</div>
<div class="logger2">Dark Sortable Receive</div>
The main idea is that, as you can see, is that the dark sortable is above the first one, it is positioned using position: absolute and z-index. I have a little bit of opacity for the black one, just so I can look what happens with the first sortable.
Below the bottom red square we have two indicators that shows when receive event is fired on the specific sortable.
Please note the fact that when you drop an item onto the blue sortable, only indicator for this sortable is fired. When I drop a new item onto the black a receive event is fired on both sortables.
Is this behavior a desired one? I think, in this case just the dark one have to receive this event.
I wonder if this is not jQuery UI bug. Ticket.
Is there any knockout plugin for showing a nested context menu?
My specific need is to show a contextmenu for list items, that has a "SendTo" menuitem and the possible subItems have to be set at runtime.
In the apparent lack of suitable plugins I went for the more direct Knockout approach. I'm sure the code can be more elegantly packed in a custom binding but this is the solution I came up with, if anyone would need something similar. In my case the menu is build each time it's opened because it makes sense in my usecase. I'm sure the code can easily be customized to some other scenarios.
So... The markup:
<li data-bind="event: { contextmenu: $root.showSendToMenu }/>
is used to show the popup menu, for an item on a right click.
And the markup for the menu itself:
#*The send to popup menu*#
<ul class="context-menu" data-bind="visible:contextMenu.activeElement, style:{left:contextMenu.left, top:contextMenu.top}, template: { name: 'menu-item-template', foreach: contextMenu.items }, event: { mouseleave: contextMenu.contextMouseLeave }"></ul>
#*Template for a menu item*#
<script type="text/html" id="menu-item-template">
<li>
<!-- ko if: children -->
<span data-bind="text:text"></span>
<ul data-bind="template: { name: 'menu-item-template', foreach: children }"></ul>
<!-- /ko -->
<!-- ko if: !children -->
<!-- /ko -->
</li>
</script>
And in the viewModel, I have the following code (TypeScript):
contextMenu = { activeElement: ko.observable(null), left: ko.observable('0px'), top: ko.observable('200px'), items: ko.observableArray(<MenuItem[]>[]), contextMouseLeave : () => { this.contextMenu.activeElement(null); } };
showSendToMenu = (item, event) => {
//Set menu position
this.contextMenu.left(event.pageX + 'px');
this.contextMenu.top(event.pageY + 'px');
//Build the menu
var lines = [];
for (var i = 0; i < this.prodLines().length; i++) {
var line = this.prodLines()[i];
if (line.lists().length > 0) {
var lists = [];
for (var j = 0; j < line.lists().length; j++) {
var list = line.lists()[j];
lists.push(new MenuItem(list.name(), null, list));
}
lines.push(new MenuItem(line.name + "->", lists, line));
}
}
var items = [new MenuItem("SendTo ->", lines)];
//Set the menu
this.contextMenu.items(items);
this.contextMenu.activeElement(item);
}
onContextClick = (menuItem: MenuItem) => {
var sendToList = menuItem.tag;
var item = this.contextMenu.activeElement();
this.dropToList(item, sendToList);
//Hide the menu
this.contextMenu.activeElement(null);
}
And lastly, I used this piece of css to make the menu behave menu-like:
.context-menu {
position: absolute;
padding: 0;
margin: 0;
z-index: 1030;
background-color: #dddddd;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3);
min-width:100px;
border: 1px solid gray;
text-decoration:none;
}
.context-menu ul {
position: absolute;
z-index: 1031;
line-height: 1.6;
padding: 0;
margin: 0;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3);
display:none;
left:98px;
min-width:100px;
top:-1px;
background-color:#dddddd;
border: 1px solid gray;
text-decoration:none;
}
.context-menu li {
position:relative;
}
.context-menu li:hover > ul {
display:block;
}
.context-menu li {
padding: 4px 20px;
margin: 0;
list-style-type: none;
cursor: pointer;
white-space: nowrap;
color: #333333;
}
.context-menu ul > li:hover {
background-color: white;
text-decoration:underline;
}
I hope this will help someone
Seems like I can choose only 2. :)
Here's where I'm at. I can sort the droppable divs and I can drag the ui-draggable, but I cannot drop ui-draggable into the ui-widget-header divs. :/
HTML:
<div class="large-9 small-9 columns">
<div id="paginationPageDisplay" class="dropme paginationContainer ui-sortable">
<div class="droppable ui-widget-header">
<div class="droppable ui-widget-header">
</div>
</div>
<div class="large-3 small-3 columns">
<div id="paginationAdDisplay" class="paginationContainer">
<div class="adThing ui-draggable">1/4</div>
<div class="adThing ui-draggable">1/2</div>
<div class="adThing ui-draggable">Full Page</div>
</div>
</div>
CSS:
.paginationContainer {
height: 1500px;
margin-right: 1px;
border: 1px solid #CCCCCC;
}
.adThing {
display: inline-block;
width: auto;
height: auto;
padding: 8px 15px;
border: 1px solid #ccc;
text-align: center;
background: #eee;
}
.dropme {
display: inline-block;
width: auto;
height: auto;
overflow: hidden;
margin-top: 20px;
}
.droppable {
height: 120px;
width: 90px;
padding: 2px;
margin: 10px 3px;
background: #F9F9F9;
border: 1px solid #CCCCCC;
float: right;
}
JS:
$('.adThing').draggable({
cursor: 'pointer',
connectWith: '.dropme',
helper: 'clone',
opacity: 0.5,
zIndex: 10
});
$('.dropme').sortable({
connectWith: '.dropme',
cursor: 'pointer'
});
$('.droppable').droppable({
drop: function( event, ui ) {
$( this )
.addClass( "ui-state-highlight" )
.find( "p" )
.html( "Dropped!" );
}
});
$("#paginationLoadButton").click(function() {
var pageCount, i, $pageDisplay = $("#paginationPageDisplay"), pageWidget;
$pageDisplay.html('');
pageCount = $("#paginationPageCount").val();
for (i=1; i<=pageCount; i++) {
pageWidget = '<div class="droppable ui-widget-header">' + i + '<p></p></div>';
$pageDisplay.html($pageDisplay.html() + pageWidget);
}
});
The problem was that I was binding droppable on document ready but those elements are generated by the button click. I moved the droppable bind into the button click event and it's resolved.
$("#paginationLoadButton").click(function() {
var pageCount, i, $pageDisplay = $("#paginationPageDisplay"), pageWidget;
$pageDisplay.html('');
pageCount = $("#paginationPageCount").val();
for (i=1; i<=pageCount; i++) {
pageWidget = '<div class="droppable ui-widget-header">' + i + '<p></p></div>';
$pageDisplay.html($pageDisplay.html() + pageWidget);
}
$('.droppable').droppable({
drop: function( event, ui ) {
$( this )
.addClass( "ui-state-highlight" )
.find( "p" )
.html( "Dropped!" );
}
});
});