4

Created a text area in svg using joint js. However, I am not able to enter any text in the text area. How to make the text area editable?

Code:

var graph = new joint.dia.Graph;

                var paper = new joint.dia.Paper({
                    el: $('#myholder'),
                    width: 1500,
                    height: 700,
                    model: graph
                });

                // Create a custom element.
                // ------------------------
                joint.shapes.html = {};
                joint.shapes.html.Element = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
                    markup: '<g class="rotatable"><g class="scalable"><rect/></g><g class="inPorts"/><g class="outPorts"/></g>',
                    portMarkup: '<g class="port<%=1%>"><circle/></g>',
                    defaults: joint.util.deepSupplement({
                        type: 'html.Element',
                        size: {width: 100, height: 80},
                        inPorts: [],
                        outPorts: [],
                        attrs: {
                            '.': {magnet: false},
                            rect: {
                                stroke: 'none', 'fill-opacity': 0, width: 150, height: 250,
                            },
                            circle: {
                                r: 6, //circle radius
                                magnet: true,
                                stroke: 'black'
                            },
                            '.inPorts circle': {fill: 'green', magnet: 'passive', type: 'input'},
                            '.outPorts circle': {fill: 'red', type: 'output'}
                        }
                    }, joint.shapes.basic.Generic.prototype.defaults),
                    getPortAttrs: function (portName, index, total, selector, type) {

                        var attrs = {};
                        var portClass = 'port' + index;
                        var portSelector = selector + '>.' + portClass;
                        var portCircleSelector = portSelector + '>circle';
                        attrs[portCircleSelector] = {port: {id: portName || _.uniqueId(type), type: type}};
                        attrs[portSelector] = {ref: 'rect', 'ref-y': (index + 1) * (10 / total)};
                        if (selector === '.outPorts') {
                            attrs[portSelector]['ref-dx'] = 0;
                        }
                        return attrs;
                    }
                }));


                // Create a custom view for that element that displays an HTML div above it.
                // -------------------------------------------------------------------------

                joint.shapes.html.ElementView = joint.dia.ElementView.extend({
                    template: [
                        '<div class="html-element">',
                        '<button class="delete">x</button>',
                        '<span id="lbl" value="Please write here"></span>',
                        '<textarea id="txt" type="text" value="Please write here"></textarea>',
                        '</div>'
                    ].join(''),
                    initialize: function () {
                        _.bindAll(this, 'updateBox');
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);

                        this.$box = $(_.template(this.template)());
                        // Prevent paper from handling pointerdown.
                        this.$box.find('input,select').on('mousedown click', function (evt) {
                            evt.stopPropagation();
                        });


                        // This is an example of reacting on the input change and storing the input data in the cell model.
                        this.$box.find('textarea').on('change', _.bind(function (evt) {
                            this.model.set('textarea', $(evt.target).val());
                        }, this));
                        this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
                        // Update the box position whenever the underlying model changes.
                        this.model.on('change', this.updateBox, this);
                        // Remove the box when the model gets removed from the graph.
                        this.model.on('remove', this.removeBox, this);

                        this.updateBox();

                        this.listenTo(this.model, 'process:ports', this.update);
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);
                    },
                    render: function () {
                        joint.dia.ElementView.prototype.render.apply(this, arguments);
                        this.paper.$el.prepend(this.$box);
                        // this.paper.$el.mousemove(this.onMouseMove.bind(this)), this.paper.$el.mouseup(this.onMouseUp.bind(this));
                        this.updateBox();
                        return this;
                    },
                    renderPorts: function () {
                        var $inPorts = this.$('.inPorts').empty();
                        var $outPorts = this.$('.outPorts').empty();

                        var portTemplate = _.template(this.model.portMarkup);

                        _.each(_.filter(this.model.ports, function (p) {
                            return p.type === 'in'
                        }), function (port, index) {

                            $inPorts.append(V(portTemplate({id: index, port: port})).node);
                        });
                        _.each(_.filter(this.model.ports, function (p) {
                            return p.type === 'out'
                        }), function (port, index) {

                            $outPorts.append(V(portTemplate({id: index, port: port})).node);
                        });
                    },
                    update: function () {

                        // First render ports so that `attrs` can be applied to those newly created DOM elements
                        // in `ElementView.prototype.update()`.
                        this.renderPorts();
                        joint.dia.ElementView.prototype.update.apply(this, arguments);
                    },
                    updateBox: function () {
                        // Set the position and dimension of the box so that it covers the JointJS element.
                        var bbox = this.model.getBBox();
                        // Example of updating the HTML with a data stored in the cell model.
                        // paper.on('blank:pointerdown', function(evt, x, y) { this.$box.find('textarea').toBack(); });
                        this.$box.find('span').text(this.model.get('textarea'));
                        this.model.on('cell:pointerclick', function (evt, x, y) {
                            this.$box.find('textarea').toFront();
                        });
                        this.$box.css({width: bbox.width, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'});
                    },
                    removeBox: function (evt) {
                        this.$box.remove();
                    }
                });


// Create JointJS elements and add them to the graph as usual.
// -----------------------------------------------------------

                var el1 = new joint.shapes.html.Element({
                    position: {x: 600, y: 250},
                    size: {width: 170, height: 100},
                    inPorts: ['in'],
                    outPorts: ['out'],
                    textarea: 'Start writing'
                });

                var el2 = new joint.shapes.html.Element({
                    position: {x: 600, y: 400},
                    size: {width: 170, height: 100},
                    inPorts: ['in'],
                    outPorts: ['out'],
                    textarea: 'Start writing'
                });

                graph.addCells([el1, el2]);

Also is it possible to scale the svg shape based on the text inside text area?

  • I guess you should create a custom element using html text area. I am not sure it supports all the updated browsers. Better consult with roman or dave. –  Jul 28 '15 at 11:01
  • http://stackoverflow.com/users/2921495/roman –  Jul 28 '15 at 11:05
  • Do you mind creating a separate question about the resizing elements based on the text inside? – Roman Jul 28 '15 at 18:20

1 Answers1

2

I assume that you are using the CSS stylesheet from the JointJS HTML tutorial (http://jointjs.com/tutorial/html-elements).

Note that .html-element has pointer-events set to none. With that being set all events are propagated to the (JointJS) SVG Element under the HTML Element. The paper therefore knows what element was interacted with and e.g. can start dragging it.

.html-element {
   pointer-events: none;
}

I suggest to create an exception for the TextArea by adding the following CSS rule.

.html-element textarea {
   pointer-events: all;
}
Roman
  • 1,652
  • 11
  • 15
  • Is it possible to use svg text and set content editable to true instead of html text area? –  Jul 29 '15 at 06:32
  • Yes and no. `No` because SVG Text Element has no contentEditable functionality. `Yes` as there is a hacky solution exist - please see https://github.com/artursapek/mondrian/issues/12 and http://stackoverflow.com/questions/20429580/simulating-contenteditable-in-svg-without-foreignobject. Apparently there are some issues with the cursor and I'm not sure about the cross-browser compatibility. SVG 2.0 might help here once implemented. – Roman Jul 29 '15 at 08:17
  • Check this once: http://rigrr.rapilabs.com/ where round rectangles takes text input. I am not able figure out how this is working where svg specification doesn't mention editable text –  Jul 29 '15 at 08:21
  • Also check this W3C spec please: http://www.w3.org/TR/SVGMobile12/single-page.html#text-text-edit –  Jul 29 '15 at 08:22
  • 1
    The Rapilabs draws an HTML input in front of the SVGTextElement and set opacity to 0 to that SVGTextElement. – Roman Jul 29 '15 at 08:25
  • Posted a new question about shape scaling based on text content: http://stackoverflow.com/questions/31695146/how-to-resize-svg-shape-based-on-text-content –  Jul 29 '15 at 08:28
  • Thanks for checking the link –  Jul 29 '15 at 08:29
  • 1
    The W3C link talks about SVG Tiny 1.2, which is a subset of SVG 1.1 with several additions. It can not be applied here. – Roman Jul 29 '15 at 08:55
  • In Rapilabs, when I inspect the elements in chrome, I am not able to find any html tags for text. How's this possible? It is showing only svg text –  Jul 29 '15 at 13:59
  • When you are in edit mode an `input` is appended to `body`. When you leave edit mode the `input` is removed. – Roman Jul 29 '15 at 14:11