How to change elements being copied on dragging using Dragula JS Plugin - javascript

I have a page which is similar to page builder template editor. Im using Dragula.JS as a plugin for drag and drop and used their method copy to copy elements from other container. This is what it looks like:
The problem is when I drag from right side columns and put in the left box elements are copied exactly what it is on the right columns. This is my code:
<div id="2col" class="collapse column-choices">
<a href="#" class="list-group-item">
<div class="row">
<div class="layoutBorder one-half"></div>
<div class="layoutBorder one-half"></div>
</div>
</a>
<a href="#" class="list-group-item">
<div class="row">
<div class="one-four layoutBorder"></div>
<div class="three-four layoutBorder"></div>
</div>
</a>
<a href="#" class="list-group-item">
<div class="row">
<div class="three-four layoutBorder"></div>
<div class="one-four layoutBorder"></div>
</div>
</a>
</div>
and my JS:
// the `templateContainer is the right box container`
dragula([document.getElementById('2col'), document.getElementById('templateContainer')], {
copy: true,
});
When I drag things to left box container this code will be put on:
<a href="#" class="list-group-item">
<div class="row">
<div class="layoutBorder one-half"></div>
<div class="layoutBorder one-half"></div>
</div>
</a>
That is not I want. Question is How to copy elements from right container and when put to left box container elements are going to changed this my aim. I will change elements to:
<div class="element-to-paste">
This thing will be copy on the left box. From Right.
</div>
Please point me on other drag and drop plugin that can make my objective.

The way to do what your asking is to utilize the .drop Dragula callback.
.on("drop", function(el, container, source) {}
https://github.com/bevacqua/dragula#drakeon-events
I've built one app where the 'drop zone' only had one column, so all elements would be lined-up vertically.. Similar to a sortable list. I used Angular for my project and my whole drop-zone used an ng-repeat directive, which is just a way to loop through an array of data. For an example it could be:
var data = [
{
index: 1,
type: 'image',
data: 'image.jpg'
},
{
index: 2,
type: 'textblock',
data: 'lorem ipsum, blah blah'
}]
Then in your ng-repeat directive you can read the type property, and put in some HTML for it, like
<div ng-if="mydata.type='image'">
<img src='{{ mydata.data}}'>
</div>
<div ng-if="mydata.type='text'">
<p>{{ mydata.data }}</p>
</div>
To get the correct order for your elements, you could use the ng-orderBy directive using the object's index.
This way the dropping action is a facade. You actually remove the dragged element from the DOM and replace it with a new one.
Dragula appends a .gu-transit class to elements while they're being dragged, so within your .drop callback you can loop through the DOM elements in your drop-container, find the .gu-transit, then you know the index that it's at, and can assign it a correct index property when you push it into your data array.
I'm using Angular as an example because that's what I used, and I think using it or another framework substantially helped implement this functionality. I think it'd be a lot more difficult to do with straight jQuery.

Related

How to append a bootstrap dropdown with a div based on ID?

In my angular, I have a dropdown component. Here is how it looks like:
<div dropdown placement="bottom left" #dropdown="bs-dropdown" [autoClose]="true"
[insideClick]="true" [container]="body">
<a class="templates-icon" dropdownToggle (click)="toggleTextTemplates();">
<i #one class="ic-text-templates" aria-hidden="true"></i>
</a>
<ng-container *ngIf="toggle" >
<div *dropdownMenu class="templates-container dropdown-menu"
role="menu" aria-labelledby="button-basic">
</ng-container>
</div>
Here in this example, container attribute is set to body and therefore the dropdown is bind with the body and visible top of each element.
Now let say I have three layer in my app. Layer-one, layer-two and layer-three and they are top of each other. Now I would like to display the dropdown only on layer-two (or imagine in middle layer). So I would like to bind this dropdown with layer-two based on id, so that drodown will be visible on top of layer one and two, but not the layer three:
<div id="layer-one">
some element 1
<div id="layer-two">
some element 2
<div id="layer-three"> some element 3</div>
</div>
</div>
Can someone please give me an idea, or solution how I can achieve that?
Have you tried to just place the dropdown at layer 2? otherwise you can can use javascript like: getElementByID('#33').innerhtml
or make CSS index rules, where there is a attribute called index: (-1) or (1) etc. for the placement of the dropdown

Save gridster layout using angularjs

I am creating dashboard application with Gridster: https://github.com/ManifestWebDesign/angular-gridster
I would like to save my layout to database using JSON. What I do know, is that I store charts array to database and fetch it. Is it possible to store widget col,row and size to specific widget so I could then give the size-x and size-y with angular style {{chart.xsize}}. When creating widget I could then assign default size values and save only after user has resized or dragged widget. Or is this completely wrong way to do this? How else I could store the widget sizes and positions to database?
I have ng-repeat for my widgets like this:
<div ng-if="chart.type === settings.types.LINEAR_GAUGE">
<div class="panel c8y" gridster-item size-x="2" size-y="1">
<div class="panel-heading">
<h3 class="panel-title">{{chart.title}}</h3>
<button class="btn btn-danger pull-right btn-xs" ng-click="onClickDelete($index)"><span class="glyphicon glyphicon-remove"/></button>
</div>
<div class="panel-body">
<c8y-linear-gauge dp="chart.dp" measurement="chart.data[0].measurement"/>
</div>
</div>
</div>
I've actually built several angular dashboards using angular-gridster, and save the layout back to the database. Here is how I do it:
<div gridster="gridsterOpts">
<ul>
<li
gridster-item
row="element.posY"
col="element.posX"
size-x="element.width"
size-y="element.height"
ng-repeat="element in elements track by $index">
<div class="element-title">
<!--some header stuff goes here-->
</div>
<div class="element-useable-area">
<!--Main widget stuff goes here-->
</div>
</li>
</ul>
</div>
So I have an array called elements where I am storing my widget objects. Each object in the array includes properties for the height and width and size. So, something like this:
{
posY: 0,
posX: 0,
width: 100,
height: 100,
templateUrl: ...,
data: ...
}
Fortunately because of angular binding, when the user changes the gidster item size or position, it changes the property value too! Its important to note that the elements array is appended to a $sessionStorage object from the ngStorage framework I use, so that all the changes will be persisted if the page refreshes.
Eventually, when the user saves the dashboard, I write an object to the database which includes the elements array. Thus saving all the location and size info. The next time you call that object from the database, and populate the elements array with its data, you get back the same dashboard.

Applying javascript to a grid editor

I'm using Umbraco to build my website and I defined a grid editor with its partial view, and also defined some javascript code to be applied to that grid editor. The javascript consists on applying some effects while hover. In my page I managed to work with the effects, but as I have several of these grid editors in my page, the effect gets replicated in all of them, so when I mouseover one of them, all the remaining grid editors from the same type get the effects too. For now I put my script at the Master.cshtml. How can I apply the effect to the grid editor I mouseover at that time? I managed to do something similar with other grid editor that I defined, but for this I didn't need javascript and everything worked fine.
Here is my code:
Script:
#foreach(var item in Model.Items) {
<div class="work-item work-item-box">
<a href=#Umbraco.TypedContent(item.GetValue<string>("workPage")).Url>
<div class="item item-image">
<img class="item item-image-img" src=#Umbraco.TypedMedia(item.GetValue<string>("image")).Url/>
</div>
<div class="item item-text" height="160" width="310">
<div class="item item-text-title">#Html.Raw(item.GetValue("company"))<br></div>
<div class="item item-text-description">#Html.Raw(item.GetValue("description"))</div>
</div>
</a>
</div>
}
My script:
<script>
$( document ).ready(function() {
$(".item").hover(function(){
$(".item-text").toggleClass('item-text-effect');
$(".item-image").toggleClass('item-image-effect');
$(".item-image-img").toggleClass('item-image-effect-img');
$(".item-text-title").toggleClass('item-text-title-effect');
$(".item-text-description").toggleClass('item-text-description-effect');
});
});
</script>
Thanks in advance!
Look at using 'this' to only target elements within whatever element you're hovering: How to get the children of the $(this) selector?

Remove style attribute from all descendants in jquery

I'm brand new to javascript/jquery, but have been going okay so far (though you'd hate to see my code), but I've hit a wall with trying to strip out style tags from some HTML I'm trying to clone.
The reason for cloning is that the CMS I'm forced to use (which I don't have access to code behind, only able to add code over the top) automatically builds a top nav, and I want to add a duplicate sticky nav once the user scrolls, but also add a couple of elements to the scrolled version.
The original HTML of the top nav looks a bit like like:
<nav id="mainNavigation" style="white-space: normal; display: block;">
<div class="index">
Participate
</div>
<div class="index" style="margin-right: 80px;">
News
</div>
<div class="index active" style="margin-left: 80px;">
<a class="active" href="/about/">About</a>
</div>
<div class="external">
Collection
</div>
<div class="index">
Contact
</div>
</nav>
I had mild success (other than those style tags I want to remove) with the following, even though it doesn't seem to make sense to me, as I expected some of the elements would be repeated (the whole < nav >…< /nav > tag should have been within the #mainNavigation clone, no?):
var originalNavItems = $('#mainNavigation').clone().html();
$("#site").prepend('
<div id="ScrollNavWrapper">
<div class="nav-wrapper show-on-scroll" id="mainNavWrapper">
<nav id="newScrolledNav" style="white-space: normal; display: block;">
<div class="index home">
Home
</div>
' + originalNavItems + '
<div class="newItem">
<a href="http://www.externalsite.com">
View on External Site
</a>
</div>
</nav>
</div>
</div>');
I've tried to use a few answers from related questions on here, but I keep getting incorrect results. Can you help me?
You can strip the style elements like so:
var el = $('#mainNavigation'); // or whatever
el.find('[style]').removeAttr('style');
You can use
var originalNavItems = $('#mainNavigation').clone().find("*").removeAttr("style");
Then you can use .append() to add that html elements
Fiddle
You can clone into an imaginary div and then fetch the mainNavigation also. You can also remove the style attributes along with that. Hope this works for you...
var temp = $('<div />').html($('#mainNavigation').clone());
temp.find('*').removeAttr('style');
originalNavItems = temp.html();
The nav is cloned but the html() function only returns the HTML for the contents and that's why it disappears. You can avoid some string manipulation by adding the cloned element directly before a target element.
$("#site").prepend('
<div id="ScrollNavWrapper">
<div class="nav-wrapper show-on-scroll" id="mainNavWrapper">
<nav id="newScrolledNav" style="white-space: normal; display: block;">
<div class="index home">
Home
</div>
<div class="newItem">
<a href="http://www.externalsite.com">
View on External Site
</a>
</div>
</nav>
</div>
</div>');
$('#mainNavigation').clone()
.find('[style]').removeAttr('style').end()
.insertBefore('#newScrolledNav .newItem');
In the previous case find('[style]') matches elements that have a style attribute.
I'm new to Stack Overflow (and js in general), so this might be really bad ettiquette, but I seem to have accidentally fixed it myself trying to debug my implementation of the first upvoted answer that #Anoop Joshi gave above. Please comment and let me know if it would have been better to just edit my question!
I decided to break the process down into separate steps – similar to #Kiran Reddy's response actually, but I hadn't got to trying his yet.
I tried:
var styledHTML = $('#mainNavigation').clone();
styledHTML.find("div[style]").removeAttr('style');
var originalNavItems = styledHTML.html();
$("#site").prepend('<div… etc.
with a console.log(styledHTML) etc under each line to check what I had at each stage – and it worked! (The code did, console.log didn't?)
I was just doing this to try and log the value of the variables at each stage, but whatever I did fixed it…
Now I need to figure out why I can't even make console.log(variable); work :-/
Try this code
$('#mainNavigation').children().removeAttr('style');
Hope this will help you.

Knockout-Sortable: Exclude item from sort list

I'm sorting a collection using knockout-sortable and I have a "new" row that should always remain empty and at the bottom of the list. I can cancel drops "after" this row by checking positions in beforeMove, but it would be even better if I could prevent the area below this row from appearing as a droppable target in the first place.
Is this possible? The row itself is a real element in the collection I'm sorting on and is in the container I'm binding with sortable:, and these things would be very difficult to change.
<div id="main">
<h3>Tasks</h3>
<div class="container" data-bind="sortable: tasks">
<div class="item">
<span data-bind="visible: !$root.isTaskSelected($data)">
</span>
<span data-bind="visibleAndSelect: $root.isTaskSelected($data)">
<input data-bind="value: name, event: { blur: $root.clearTask }" />
</span>
</div>
</div>
Add Task
</div>
A basic sortable JSFiddle. I want dropping below the "Enter a new task" row to be impossible.
You can use the items option of jQuery UI's sortable for this purpose.
The idea would be that you place a class on the items that you actually want to be sortable and then use the items option to select them.
This class could be from a computed. The computed in your case could potentially be related to the last item in the list.
When you bind, it would look like: sortable: { data: items, options: { items: '.good-items' } }
Here is a basic sample: http://jsfiddle.net/rniemeyer/mBjHN/

Categories