Is it possible to have nested input text boxes?

2,181 views
Skip to first unread message

Bruce

unread,
Apr 29, 2014, 12:23:01 AM4/29/14
to jst...@googlegroups.com
I'm trying to have nested input text boxes using jstree. Something like the below image: 



The values in the text boxes need to be editable - is this possible to accomplish with the current options, and if it's not - is the best approach to try and make a plugin?

Ivan Bozhanov

unread,
Apr 29, 2014, 3:30:22 AM4/29/14
to jst...@googlegroups.com
Well, you could try supplying the HTML for each box in the markup (or the JSON "text" property), that will probably not work though, as the textbox will be nested in a ANCHOR node, which will make it hard to focus and work with. Your best approach would be as you said, to create a plugin. Have a look at src/misc.js for examples and let me know if you need help. It should be pretty straight forward, but as I said - if you need help - just let me know.

Best regards,
Ivan

Bruce

unread,
Apr 30, 2014, 8:14:47 PM4/30/14
to jst...@googlegroups.com
So I started on this - I was thinking of starting by simply letting the user edit the "name" field and then super-imposing a textbox image over the <li> structure using CSS...

var _b = document.createElement('B');
_b.className = 'jstree-icon jstree-inputbox';

$.jstree.defaults.inputbox = {
/**
* a boolean indicating if input boxes should be visible. Defaults to `true`.
* @name $.jstree.defaults.inputbox.visible
* @plugin inputbox
*/
visible : true,
};
$.jstree.plugins.inputbox = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
this.element
.on("init.jstree", ".jstree-anchor", $.proxy(function () {
this._data.inputbox.visible = this.settings.inputbox.visible;
}, this))
.on("loading.jstree", $.proxy(function () {
// this[ this._data.inputbox.visible ? 'show_inputboxes' : 'hide_inputboxes' ]();

}, this))
.on("click.jstree", $.proxy(function (e) {
// this[ this._data.inputbox.visible ? 'show_inputboxes' : 'hide_inputboxes' ]();
// this.element.addClass('jstree-inputbox');
var obj = this.get_node(e.target);
this.edit(obj);
}, this));
};
this.teardown = function () {
if(this.settings.inputbox) {
this.element.find(".jstree-inputbox").remove();
}
parent.teardown.call(this);
};
/**
* show the node inputbox icons
* @name show_inputboxes()
* @plugin inputbox
*/
this.show_inputboxes = function () { this._data.core.themes.inputboxes = true; this.get_container_ul().removeClass("jstree-no-inputboxes"); };
/**
* hide the node inputbox icons
* @name hide_inputboxes()
* @plugin inputbox
*/
this.hide_inputboxes = function () { this._data.core.themes.inputboxes = false; this.get_container_ul().addClass("jstree-no-inputboxes"); };
};

1)  So in the "click.jstree" event, I can let the user edit via "this.edit(obj);" but for some reason the edits won't stick...how do I save the edit?

Bruce

unread,
Apr 30, 2014, 11:03:06 PM4/30/14
to jst...@googlegroups.com
I tried something like this:
.on("changed.jstree", $.proxy(function (e) {
 this.edited_text = this.get_text(this.edited_node);
 
 this.set_text(this.edited_node, this.edited_text);
}, this))

but the text fields keep reverting back to the previous values.
Message has been deleted

Ivan Bozhanov

unread,
May 1, 2014, 2:50:14 AM5/1/14
to jst...@googlegroups.com
It appears I misunderstood you the first time - I thought you needed inputboxes on each LI node, that would be editable always (I did not know you wanted to do something with this.edit). Please tell me what you need to do so that I can help you. In the mean time - this is what I imagined:
(function ($, undefined) {
    "use strict";
    var inp = document.createElement('INPUT');
    inp.setAttribute('type','text');
    inp.className = "jstree-inp";
    inp.style.width = '40px';

    $.jstree.plugins.inp = function (options, parent) {

        this.bind = function () {
            parent.bind.call(this);
            this.element
                .on("change.jstree", ".jstree-inp", $.proxy(function (e) {
                        // do something with $(e.target).val()

                    }, this));
        };
        this.teardown = function () {
            if(this.settings.questionmark) {
                this.element.find(".jstree-inp").remove();
            }
            parent.teardown.call(this);
        };
        this.redraw_node = function(obj, deep, callback) {
            obj = parent.redraw_node.call(this, obj, deep, callback);
            if(obj) {
                var tmp = inp.cloneNode(true);
                obj.insertBefore(tmp, obj.childNodes[1]);
            }
            return obj;
        };
    };
})(jQuery);

Which looks like this:



Best regards,
Ivan

Ivan Bozhanov

unread,
May 1, 2014, 2:50:44 AM5/1/14
to jst...@googlegroups.com

Bruce

unread,
May 2, 2014, 1:49:31 AM5/2/14
to jst...@googlegroups.com
Thank you Ivan - I was able to achieve what I wanted using the code above...I went down this.edit because I'm new to Javascript so I pattern my code after what I see and get better as I go along. 
 
In this.element.on (some event.....) {
var obj = this.get_node('#');
values
[obj.children_d[i]]=$("input#"+obj.children_d[i]).val();
}
// below I set the id of the input box via tmp
this.redraw_node = function(obj, deep, callback) {
          obj = parent.redraw_node.call(this, obj, deep, callback);
          if(obj) {
              var tmp = inputbox.cloneNode(true);
              tmp.id = obj.id;
              obj.insertBefore(tmp, obj.childNodes[1]);
          }
          return obj;
      };

I do something like the above to get/set values for the input text boxes. 
1)  Is there a way to reference the input boxes without having to use $(#input+...) [i.e., the #input id] ?  It seems like there might be (your code is so nicely put together I think)....  
2)  How do you decide what plugins to add to the official repo?

Ivan Bozhanov

unread,
May 2, 2014, 4:08:19 AM5/2/14
to jst...@googlegroups.com
I will add this to the repo, but not in the build - it will be available in the scr/misc.js file - a collection of plugins that I (or other users in the group make) but it will not be included in the combined file.
As for the value - try using $(e.target).val();

Best regards,
Ivan

Robb Merritt

unread,
Mar 21, 2015, 12:49:46 PM3/21/15
to jst...@googlegroups.com

Thanks guys!  I was struggling with this myself.  There is some css style issues to deal with in addition to the plugin Ivan provided.  I decided I wanted a drop-down only on the root nodes. instead of text so this what I came up with.  I put it on git hub.  I think I will be adding a something to edit a description.  I am using this with angularJS and it looks great!  Thank you.

https://github.com/TransEmpiric/jstree-selector

(function ($, undefined) {
    "use strict";
    $.jstree.plugins.selector = function (options, parent) {

        this.bind = function () {
           
            parent.bind.call(this);
            this.element.on("change", ".jstree-selector", $.proxy(function (e) {
                var node = this.get_node($(e.target));
                $(e.target).trigger( "change_selector.jstree", [node, $(e.target)]);

            }, this));
           
        };
        this.teardown = function () {
            if(this.settings.questionmark) {
                this.element.find(".jstree-selector").remove();

            }
            parent.teardown.call(this);
        };
        this.redraw_node = function(obj, deep, callback) {
            var node = this.get_node(obj);
            var param = this.settings.selector.param;
            var array = this.settings.selector.options;
            var select = document.createElement("SELECT");
            select.id = "selector";
            select.className  = "jstree-selector";
            select.setAttribute("style", "appearance: button;-moz-appearance: button;" +
                    "    -webkit-appearance: button;height: auto;width:100px;position:relative;" +
                    "color:inherit;text-decoration:none;display:inline-block;" +
                    "vertical-align:top;white-space:nowrap;margin-left:10px;");

            obj = parent.redraw_node.call(this, obj, deep, callback);
            if(node.parent === '#') {
                for (var i = 0; i < array.length; i++) {
                    var option = document.createElement("OPTION");
                    option.value = array[i].value;
                    option.text = array[i].text;
                    if(node.data != null && array[i].value == node.data[param]){
                        option.setAttribute("selected", true)
                    }
                    select.appendChild(option);
                }

                var tmp = select.cloneNode(true);
                obj.insertBefore(tmp, obj.childNodes[3]);
                array = [];
            }
            return obj;
        };
    };
})(jQuery);

Chema Liso

unread,
Jul 6, 2015, 5:02:24 AM7/6/15
to jst...@googlegroups.com
Hello, I have tested the solution and it works fine. Just a question: i don't know how to keep the values for each input tag, or how to relation each input tag with his nested checkbox tag. If I close the parent node and reopen it, all values previously introduced are lost.

Thank you.

Ivan Bozhanov

unread,
Jul 6, 2015, 5:06:50 AM7/6/15
to jst...@googlegroups.com, josemar...@gmail.com
You have to implement such functionality inside the plugin code posted above.

Best regards,
Ivan

Geo

unread,
Jul 17, 2015, 5:08:47 PM7/17/15
to jst...@googlegroups.com
Hi Robb,

Could you please let me know, is it possible to implement the drop-down list to a specific level of child nodes under all the root, not to all child nodes and root nodes?
For eg: if I have multiple root nodes with child nodes of depth 4, I need to create the 4th child node with a drop down list for all the root nodes.

Thank you.
Reply all
Reply to author
Forward
0 new messages