JsTree v3.0 drag and drop plugin. Reference target node upon dropping - javascript

I use drag and drop plugin of jsTree library (ver. 3.0)
With the following code I can bind to the end of drag'n'drop action, but I can not see a way to get the reference to the target node (the node I'm dropping on).
$(document).on('dnd_stop.vakata', function(e, data) {
// how to get target_node here?
});

I had same problem. I found other solution than event dnd_stop.vakata, which returns old data before changed position.
This works:
$('#jstree_demo_div').on("move_node.jstree", function (e, data) {
//data.node, data.parent, data.old_parent is what you need
//console.log(data);
alert(data.node.id);
alert(data.parent);
});

Another solution is to use the get_node() function on the jstree object.
$(document).on('dnd_stop.vakata', function (e, data) {
ref = $('#jstree').jstree(true);
parents = ref.get_node(data.element).parent;
});
You can get all parents with:
all_parents = ref.get_node(data.element).parents;

I had the same problem and had to get the ID within the dnd_stop event, so I came up with this:
$(document).on('dnd_stop.vakata', function(e, data) {
var t = $(data.event.target);
var targetnode = t.closest('.jstree-node');
var nodeID = targetnode.attr("id");
});
That way I can get the ID of the targetnode, for example.

$(document).on('dnd_stop.vakata', function(e, data) {
var inst = $.jstree.reference('#jstree');
console.log("END DROP:");
var sourceID = data.data.nodes[0];
console.log("Source ID: " + sourceID);
var targetNode = inst.get_node(data.event.target, true);
var targetID = targetNode[0].id;
console.log("Target ID: " + targetID);
});

If you need to do this via the check_callback then you can access the target node via the more parameter.
'check_callback': function(operation, node, node_parent, node_position, more) {
// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
// in case of 'rename_node' node_position is filled with the new node name
if (operation === "move_node") {
if (more.ref.data.type === "folder") {
return true;
}
}
return false; //allow all other operations
}

To get the target node you use the dnd_stop.vakata event. Once you get the node you can access to the different properties like id:
$(document).bind("dnd_stop.vakata",function(e, data) {
var targetNode = $('#jstree').jstree(true).get_node($(data.event.target));
var nodeId = targetNode.id;
});

you just need to call:
'check_callback': function(operation, node, node_parent, node_position, more) {
// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
if (operation === "move_node") {
var node = more.origin.get_node('fiche-1');
return true;
}
return true; //allow all other operations
}

Bind the listener after document is ready:
$(document).ready(function() {
$(document).on('dnd_stop.vakata', function (e, data) {
let ref = $.jstree.reference("#jstree");
let nodes = data.data.nodes.map(node_id => ref.get_node(node_id));
let parent_node_id = nodes[0].parent;
let parent = ref.get_node(parent_node_id);
});
});
jstree has an internal listener for dnd_stop.vakata.jstree that performs the ui logic. It's setup inside a $(function() {...}) i.e. when the document is ready. If you bind your custom function before jstree, you get the parent before the ui logic is executed.

Related

Drag and Drop Java Script Executor returns undefined

I'm testing a drag and drop feature on a web application using Selenium, and I am using a java script to carry out the dragging and dropping functionality
I have a fair understanding on the function but I am not able to wrap my head around its complete working
I am souring the web elements using Selenium's findElement method
WebElement From = driver.findElement(By.cssSelector("#todrag > span:nth-child(4)"));
WebElement To = driver.findElement(By.cssSelector("#mydropzone"));
and the below is where the action takes place
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("function createEvent(typeOfEvent) { var event = document.createEvent(\"CustomEvent\"); event.initCustomEvent(typeOfEvent, true, true, null); event.dataTransfer = { data: {}, setData: function (key, value) { this.data[key] = value; }, getData: function (key) { return this.data[key]; } }; return event; } function dispatchEvent(element, event, transferData) { if (transferData !== undefined) { event.dataTransfer = transferData; } if (element.dispatchEvent) { element.dispatchEvent(event); } else if (element.fireEvent) { element.fireEvent(\"on\" + event.type, event); } } function simulateHTML5DragAndDrop(element, target) { var dragStartEvent = createEvent('dragstart'); dispatchEvent(element, dragStartEvent); var dropEvent = createEvent('drop'); dispatchEvent(target, dropEvent, dragStartEvent.dataTransfer); var dragEndEvent = createEvent('dragend'); dispatchEvent(element, dragEndEvent, dropEvent.dataTransfer); } var elementToDrag = arguments[0]; var target = arguments[1]; simulateHTML5DragAndDrop(elementToDrag, target);",
From, To);
This code seems to work perfectly fine on "https://the-internet.herokuapp.com/drag_and_drop" but fails to work on "https://www.seleniumeasy.com/test/drag-and-drop-demo.html"
The below is the error I see on the screen
It'd be great if someone helps me understand and resolve this issue
The site's JS can be found in this link - https://easyupload.io/dgefbv

jsTree - Closing nodes on a specific level

I have a jstree with 5 levels of nodes. I want to iterate through all nodes to close those nodes for which the level is 4 so that one doesn't see level 5 unless 4 is clicked to expand. If there is a better approach than iterating, I'm all ears!
$("#mytree").bind('ready.jstree', function(event, data) {
var $tree = $(this);
$($tree.jstree().get_json($tree, {
flat: true
}))
.each(function(index, value) {
var node = $("#mytree").jstree().get_node(this.id);
var lvl = node.parents.length;
if (lvl = 4) {
node.close_node(this, true);
}
});
});
I found an answer, in case anyone else is looking to do the same.
Instead of opening all and then closing some, I started with all closed and opened the ones I wanted:
$("#mytree").bind('ready.jstree', function(event, data) {
var $tree = $(this);
$($tree.jstree().get_json($tree, {
flat: true
}))
.each(function(index, value) {
var node = $("#mytree").jstree().get_node(this.id);
var lvl = node.parents.length;
if (lvl <= 3) {
$('#mytree').jstree().open_node({"id":node.id});
}
});
});
If you want to open the nodes of first level, you may try this.
$('#myTree li').each( function() {
var node=$("#myTree").jstree().get_node(this.id);
var level = node.parents.length;
if(level<=1){
$('#myTree').jstree().open_node({"id":node.id});
}
});
It works for me, when I want to display the folders of an azure container. In my case, container is the root node.

Can not fetch instanceTree autodesk

I am getting this error: Uncaught TypeError:> Cannot read property 'getRootId' of undefined
even i am using Autodesk.Viewing.GEOMETRY_LOADED_EVENT..still no effect.
You just need to wait for Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT to be fired when you want to access the instanceTree:
viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, function () {
var instanceTree = model.getData().instanceTree //cool
})
You should not be using instanceTree data structure, but yet the functions/operations, which are the supported way. If your need is to enumerate leaf nodes, try something similar to as described here:
function getAllLeafComponents(viewer, callback) {
var cbCount = 0; // count pending callbacks
var components = []; // store the results
var tree; // the instance tree
function getLeafComponentsRec(parent) {
cbCount++;
if (tree.getChildCount(parent) != 0) {
tree.enumNodeChildren(parent, function (children) {
getLeafComponentsRec(children);
}, false);
} else {
components.push(parent);
}
if (--cbCount == 0) callback(components);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var allLeafComponents = getLeafComponentsRec(tree.getRootId());
});
}

Can I watch for changes to datalayer variables in Google Tag Manager?

I am writing a Javascript function that will be a tag in Google Tag Manager.
It is loaded on a SPA which I have minimal control over.
Whenever a user clicks, I use the GTM functionality to push some data to the datalayer, e.g.:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
// Trigger a generic "gtm.click" event on every click
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
};
Every time this is triggered, it will push a new event to the datalayer, and updated the value of gtm.customWatchedVariable. What I now want to check is if the current gtm.customWatchedVariable is different from the last gtm.customWatchedVariable, and then fire a Trigger in GTM when it changes.
How can I do this?
This JS is checking if last gtm.customWatchedVariable variables in datalayer object is different:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
var customWatcherVar = dataLayer.filter(function(e){ return typeof(e["gtm.customWatchedVariable"]) != 'undefined';});
var prevDatalayer = customWatcherVar[customWatcherVar.length-2];
var newDatalayer = customWatcherVar[customWatcherVar.length-1];
var prevVal = null;
var newVal = null;
if (prevDatalayer!=null)
{
prevVal = prevDatalayer["gtm.customWatchedVariable"];
}
if (newDatalayer!=null)
{
newVal = newDatalayer["gtm.customWatchedVariable"];
}
if (prevVal != newVal)
{
// Push another datalayer, because gtm.customWatchedVariable changed
}
};
Thanks to #victor-leontyev, for pointing me towards the answer.
I didn't realise you could treat the dataLayer object like any other array. So my code now looks like this:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
var lastEvent = dataLayer
.filter(function (e) { return e.event === 'gtm.customEvent'; })
.pop();
var lastValue = lastEvent instanceof Object
? lastEvent["gtm.customWatchedVariable"]
: false;
// Trigger a generic "gtm.click" event on every click
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
if (lastValue !== target.val()) {
// Do the thing.
}
};
Thanks!

jQuery Plugin Overwriting Parameters

This may be a very mundane question, but this is the first jQuery plugin that I have written and I'm a bit fuzzy on understanding the scope rules in JavaScript.
I'm trying to write an simple jQuery plugin that wraps around the Stack Overflow API. I'm starting off by trying to work with the Flair API.
I wanted to make the plugin as configurable as possible so that you can easily pass it the domain and user id, and generate multiple Flairs.
var superUser = $.jStackOverflow.flair({domain:"superuser.com", id: 30162, parentId:'#su-flair'});
var stackOverflow = $.jStackOverflow.flair({domain:"stackoverflow.com", id: 55954, parentId:'#so-flair'});
The problem is, when it makes the second call, it's somehow using the correct domain and id parameters, but the parentId field that it's using in the callback function to create the HTML is using the first parameter.
You can see the plugin here and the HTML here
UPDATED
DEMO: http://jsbin.com/epeti3/5
/* 16/02/2012 02.04.38 */
(function($) {
$.fn.jStackOverflow = function(options) {
var opts = $.extend({},
$.fn.jStackOverflow.defaults, options);
return this.each(function() {
$this = $(this);
var opt = $.meta ? $.extend({},
opts, $this.data()) : opts;
var result;
var id = this.id;
var flair = $.fn.jStackOverflow.flair(opt, id);
$this.html(flair);
});
};
$.fn.jStackOverflow.setApis = function(options) {
var apis = options.protocol + options.domain + options.gTLD + "/users/flair/" + options.id + "." + options.format;
if (options.makeCallbacks) {
apis += "?callback=?";
}
return apis;
};
$.fn.jStackOverflow.flair = function(options, id) {
var api = $.fn.jStackOverflow.setApis(options);
if (options.makeCallbacks) {
result = $.getJSON(api,
function(data) {
$.fn.jStackOverflow.flairCallback(data, options, id);
});
}
return result;
};
$.fn.jStackOverflow.flairCallback = function(data, options, id) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
$('<div class="' + key + '"></div>').html(key + ' : ' +data[key]).appendTo('#' + id);
}
}
};
$.fn.jStackOverflow.defaults = {
protocol: 'http://',
domain: 'stackoverflow',
gTLD: '.com',
format: 'json',
makeCallbacks: true
};
})(jQuery);
use:
<div id="so-flair"></div>
$(function() {
$('#so-flair').jStackOverflow({domain:"stackoverflow", id: 91130 });
});
The problem is that you only have a single instance of your plugin. This means that the two calls to $.jStackOverflow.flair() interfere with each other as both manipulate interal data of a single object.
Check for a demo what happens if there is some delay between the two calls (click the two buttons at the bottom)
http://jsbin.com/esovu (to edit http://jsbin.com/esovu/edit
Suddenly it starts working. So you need to investigate how to write a plugin which supports multiple instances on a single page.
You can pick any "good" jQuery plugin which multiple instances support to check how to do it.
e.g. jQuery Carousel.
Check how the lines interact to allow creating multiple Carousel instances on one page (code taken from jQuery Carousel source)
$.fn.jcarousel = function(o) { //this would match your `jStackOverflow`
return this.each(function() { //for each matched element return a new carousel
new $jc(this, o);
});
};
...
var defaults = {
...
};
...
$.jcarousel = function(e, o) { //the acutal constructor
...
}
...
$jc.fn.extend({
...
});

Categories

Resources