Using jsTree 3.0.8 I want to allow nodes to be dragged and dropped within the tree, but it should only allow that action if it was initiated by a drag on a specific button or "drag handle", instead of the default behavior of allowing dragging anywhere on the row.
I populate my tree via Html and for each node I draw out a toolbar as part of that row's Html content. I assign delegated jQuery click handlers to the toolbar items. One of the toolbar items needs to have a move icon and only allow that tree item to be moved by dragging on that toolbar button.
jQuery UI has a similar drag handle concept documented here:
https://jqueryui.com/draggable/#handle
Here is one node of my tree. The "Reorder" button is where I want the user to be able to drag the row, and nowhere else.
<li data-jstree="{ 'type': 'page' }">
<i class="jstree-icon jstree-ocl" role="presentation"></i>
<a class="jstree-anchor" href="#" tabindex="-1" id="109_anchor">
<i class="jstree-icon jstree-themeicon fa fa-2x fa-file font-grey-cascade jstree-themeicon-custom" role="presentation"></i>
<div class="pull-left">
<div class="friendly-name">test</div>
<small>/okay/its/a/test</small>
</div>
<span class="nodeToolbar">
<a title="Edit" class="passthrough btn grey-cascade btn-xs" href="/Pages/Edit/109"><i class="fa fa-pencil"></i> Edit</a>
<a title="Preview" class="preview btn grey-cascade btn-xs" target="_pagePreview" href="/Pages/Preview/109"><i class="fa fa-eye"></i> Preview</a>
<button title="Reorder" class="reorder btn grey-cascade btn-xs" href="#">
<i class="fa fa-arrows"></i> Reorder
</button>
</span>
</a>
</li>
I already took a look at some other solutions that involve check_callback but that seems to only prevent the move operation at the end (during the drop) rather than what I need, which is to filter the operation at the beginning (when the drag is initiated).
You will probably need to work with is_draggable:
http://www.jstree.com/api/#/?q=is_dragg&f=$.jstree.defaults.dnd.is_draggable
For a quick and dirty solution - listen for mousedown touchstart events on anchor nodes and set a variable which you can then later use in the is_draggable check:
var allow_drag = false;
$('#tree').on('mousedown touchstart', '.jstree-anchor', function (e) {
allow_drag = e.target.className.indexOf('reorder') !== -1;
});
$('#tree').jstree({
dnd : {
is_draggable : function () { return allow_drag; },
...
Related
I am trying to trigger a manual dropdown event in javascript. below is the function where I am working. I am stopping the first event that takes place and now i want to trigger a drop down event.
stopNavigationTriggerDropDown(event: any){
event.stopImmediatePropagation();
event.stopPropagation();
event.preventDefault();
//code that triggers manual dropdown event
}
now this is the html code:
<tr (click)="goToPlan(plan)">
<td>....</td>...
<td>
<div class="input-group">
<button (click)="stopNavigationTriggerDropDown($event)" type="button" class="btnTransparent" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-ellipsis-h"></i>
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" (click)="action(apl)">Go</a>
</div>
</div>
</td>
</tr>
I can not use jQuery just pure JavaScript. I have searched online but many examples show jQuery, but I need it to be just pure JS. I thank you guys in advance!
There are two main ways you can accomplish this.
1: Fire button's click event:
var button = document.getElementsByClassName("example");
button.click()
2: Create and dispatch an event on the page (see: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events)
var button = document.getElementsByClassName("example");
var btnEvent = new Event('click');
button.dispatchEvent(btnEvent);
I have AngularJS (1.x) component - or actually directive to be precise,
and the elements inside this directive are get fired/worked/actioned only in the second click.
For example: a dropdown will not toggle unless you click twice.
After the second click the toggle will be on each next click.
It's like the elements are not digested in the DOM.
This is the component call:
<dropdown-custom-component-example></dropdown-custom-component-example>
This is the component html file:
<div class="dropdown">
<a class="btn pl-2" data-toggle="dropdown">
<i class="icon-options"></i>
</a>
<div class="dropdown-menu">
<a class="dropdown-item" data-toggle="modal" data-target=".create-supplier">
<i class="fa icon-plus text-primary"></i>
Action
</a>
</div>
</div>
you could try to digest by your own inside your directive, after you've checked if $digest isn't alreay in progress:
if(!$scope.$$phase) $scope.$digest();
// or $apply ..
hope this solves your issue :)
Most of the time AngularJS does not respond is because something is happening outside the Angular context, in those cases it's useful to try to replicate the functionality you want with built-in AngularJS directives.
It would be useful to have access to some of the implementation of your component. However, with all the information you have provided, you can use the ngClick built-in directive:
<div class="dropdown">
<a class="btn pl-2" data-toggle="dropdown">
<i class="icon-options"></i>
</a>
<div class="dropdown-menu">
<a class="dropdown-item" data-toggle="modal" data-target=".create-supplier" ng-click="yourFunctionHere(this)">
<i class="fa icon-plus text-primary"></i>
Action
</a>
</div>
</div>
AngularJS Docs - ngClick
I want to trigger an on click event for my <i> tag. I added an ID to it but if i try use:
$("#delete-playlist-song").on("click", function() {
console.log("in"); //doesnt trigger
});
It won't trigger so I want to try a different approach? Something like:
$("master-playlist-entries").find("i.pl-action").on("click", function() {
console.log("in"); //Won't work
});
My HTML code:
<ul id="master-playlist-entries" class="database">
<li id="db-" track-name="James Darren - Goodbye Cruel World" data-path="http://example.com/Cruel%20World.mp3" class="active">
<i style="display:none;" class="fa fa-bars db-action" title="Actions" data-toggle="modal" data-target="#modal"></i>
<i id="delete-playlist-song" class="fa fa-times pl-action" title="Remove Song From Playlist"></i>
<i class="fa fa-music db-icon"></i><span class="sn"> Goodbye Cruel World</span> <span class="bl">James Darren</span></li>
</ul>
What I did try was an onclick event to call a function which worked but you see, I want to grab the data-path information and pass it to that function so I can use: $(this).attr("data-path") which will return a different link each time for different li.
Any help will be appreciated!
Your original code works in a one item snippet, https://jsfiddle.net/TrueBlueAussie/6qhhyxs7/ so I have to guess as your example is incomplete:
It is not shown, but I would guess you have multiple <i> elements with the same id (e.g. id="delete-playlist-song). If that is the case it simply will not find any except the first one as browsers use a fast-lookup cache which can only have one element stored against each ID value. IDs must be unique on a HTML page to work property.
Switch to using classes instead and use a delegated event handler.
https://jsfiddle.net/TrueBlueAussie/6qhhyxs7/1/
e.g.
$(document).on('click', '.delete-playlist-song', function() {
$(this).closest('li').slideUp();
});
Notes:
You should connect delegated event handlers to a non-changing ancestor element, but document is the best default if nothing else is close. Do not use body as it has a bug to do with styling that can cause mouse events to not fire. Use document as your friendly backup as it also exists before DOM ready.
I guess your html is added dynamically - so register the click listener dynamically using this:
$("body").on("click", "#delete-playlist-song", function() {
And for getting the attribute data-path you can use $(this).closest('li').attr("data-path") inside the listener.
See a demo below:
$("body").on("click", "#delete-playlist-song", function() {
console.log($(this).closest('li').attr("data-path"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<ul id="master-playlist-entries" class="database">
<li id="db-" track-name="James Darren - Goodbye Cruel World" data-path="http://example.com/Cruel%20World.mp3" class="active">
<i style="display:none;" class="fa fa-bars db-action" title="Actions" data-toggle="modal" data-target="#modal"></i>
<i id="delete-playlist-song" class="fa fa-times pl-action" title="Remove Song From Playlist"></i>
<i class="fa fa-music db-icon"></i><span class="sn"> Goodbye Cruel World</span> <span class="bl">James Darren</span>
</li>
</ul>
I'm using mobile angular ui to open and close a sidebar. In this sidebar a user can search for persons and add or remove these from an array.
I have this repeat that shows the array of persons when clicking on the <a ...></> it closes the sidebar:
<li ng-repeat="recipient in persons.recipients">
<span class="wrapper">
<span class="imageWrap">
<span class="initials">
{{recipient.firstName.charAt(0)}}{{recipient.lastName.charAt(0)}} </span>
</span>
<i class="fa fa-trash-o" aria-hidden="true"></i>
<span class="details">
<span class="info">
{{recipient.firstName}} {{recipient.lastName}}
<span class="persnr">{{recipient.employeeID}}</span>
</span>
</span>
</span>
</li>
The above html snippet is from a directive that is in the sidebar.
The removeRecipient($index); function looks like this:
$scope.removeRecipient = function(index) {
$scope.persons.recipients.splice(index,1);
}
The function works but closes the sidebar and I can't figure out why it does this. So each time a user removes a recipient it has to swipe the sidebar open again. How do I keep this sidebar open?
References:
mobile angular ui: http://mobileangularui.com/docs/sidebars/
SOLUTION
I solved my problem by adding $event.stopPropagation(); in the ng-click right behind the removeRecipient($index); function.
From doc, there was one line.
You can put ui-turn-off='uiSidebarLeft' or ui-turn-off='uiSidebarLeft'
inside the sidebar to make it close after clicking links inside them.
so may be you can use that or you can use or you can do like below.
e.stopPropagation()
for that you need to pass $event in
<i class="fa fa-trash-o" aria-hidden="true"></i>
so in code, you can write.
$scope.removeRecipient = function(index,e) {
if(e){
e.stopPropagation()
}
$scope.persons.recipients.splice(index,1);
}
I didn't used same tool, but may be this is issue.
I have an Angular JS 1.3 app.
In it I have a table row repeater with an ng-click:
<tr ng-repeat-start="user in users.users" ng-click="showDetails = !showDetails">
<!-- main row, with the menu td shown below -->
<tr class="row-details" ng-show="showDetails" ng-class="{ 'active': showDetails }">
<td>this is the row that hows details</td>
</tr>
<tr ng-repeat-end></tr>
Which toggles opening a detail view. Works fine. But now I need to add another item in each of the tds, a Bootstrap button menu:
<td>
<div class="btn-group">
<div ng-switch on="user.status">
<button class="btn btn-link btn-gear dropdown-toggle" type="button" data-toggle="dropdown"><i class="fa fa-cog"></i></button>
<ul class="dropdown-menu dropdown-menu-right" role="menu" ng-switch-when="Inactive">
<li>Delete</li>
</ul>
<ul class="dropdown-menu dropdown-menu-right" role="menu" ng-switch-when="Invited">
<li>Resend invitation</li>
<li>Delete</li>
</ul>
<ul class="dropdown-menu dropdown-menu-right" role="menu" ng-switch-default>
<li>Reset password</li>
<li>Delete</li>
</ul>
</div>
</div>
</td>
Problem is, trying to click on this menu button instead triggers the ng-click on the row, toggling the row state but never opening my menu. How can I prevent this?
This is pretty tricky problem. The thing is that when you click menu button two things happens: the first is that it opens Bootstrap dropdown menu, and the second is that event propagates to the parent element and also triggers showDetails = !showDetails.
The obvious solution is to try to stop event propagation with $event.stopPropagation() on the td level, so that event doesn't bubble up the DOM tree and never triggers parent ngClick. Unfortunately, it will not work because Bootstrap sets up click event listener on the document element to benefit from bubbling, so you can't stop propagation.
The simplest solution I came up with in such cases is to set a flag on the original event object whether event occurred on the menu button. If this is the case ngClick on the tr won't do anything.
It will look like this for tr:
<tr ng-repeat-start="user in users.users" ng-click="$event.originalEvent.dropdown || (showDetails = !showDetails)">
ans for button:
<button ng-click="$event.originalEvent.dropdown = true" class="btn btn-link btn-gear dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fa fa-cog"></i>
</button>
Demo: http://plnkr.co/edit/eIn6VW3Y2jHn0MtJQVcU?p=preview
With help of dfsq's answer I managed solve same kind of problem in Angular 2. Here is my sample code.
<td class="tablerowdata" (click)="$event.dropdown || onSelect(track)" >
<button (click)="$event.dropdown = true" type="button" class="btn btn-default dropdown-toggle trackbuttons" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">