仅在Firefox中SVG/JavaScript响应缓慢:是我还是Mozilla?

仅在Firefox中SVG/JavaScript响应缓慢:是我还是Mozilla?,javascript,firefox,svg,mousemove,Javascript,Firefox,Svg,Mousemove,我编写了一个拖放SVG页面,该页面必须监视mousemove事件并更新页面(在正在拖动的矩形之间拉伸一些连接线,并在拖动一个矩形时绘制线,更改连接点) 我知道监视mousemove意味着响应大量事件。尽管如此,Chrome和Opera的响应速度非常快,Safari和IE的响应速度也非常好,但Firefox的响应速度太快,令人无法接受(当前版本的所有浏览器都在Windows7下,除了MacOS Mavericks下的IE之外) 禁用加载项无效。配置文件没有显示任何明显的瓶颈,Chrome和Fire

我编写了一个拖放SVG页面,该页面必须监视mousemove事件并更新页面(在正在拖动的矩形之间拉伸一些连接线,并在拖动一个矩形时绘制线,更改连接点)

我知道监视mousemove意味着响应大量事件。尽管如此,Chrome和Opera的响应速度非常快,Safari和IE的响应速度也非常好,但Firefox的响应速度太快,令人无法接受(当前版本的所有浏览器都在Windows7下,除了MacOS Mavericks下的IE之外)

禁用加载项无效。配置文件没有显示任何明显的瓶颈,Chrome和Firefox配置文件看起来很相似。如果有人想看的话,这个页面在,但问题更多的是这是否

  • 一个已知的Firefox SVG DOM操作问题,在Mozilla更新JavaScript引擎之前没有已知的解决方法

  • 已知的Firefox问题和已知的“最佳实践”解决方案;或

  • 可能是开发人员的错误。谢谢你的指导

  • [编辑:根据@JosephDreamer的建议添加了JavaScript。试图添加一个简化的HTML/SVG文件来说明问题,结果超过了StackOverflow 30000字符的长度限制]

    "use strict";
    /*
     * Synopsis: Drag and drop support for plectograms
     * Developer: David J. Birnbaum, djbpitt@gmail.com http://www.obdurodon.org
     * Project: http://repertorium.obdurodon.org
     * Date: First version 2014-05-30
     * Last revised: 2014-06-22
     *
     * To do:
     * js:
     *   Wrap initialization in self-executing anonymous function (SEAF) to
     *     bypass addressing the window 'load' event explicitly:
     *     http://code.tutsplus.com/tutorials/key-principles-of-maintainable-javascript--net-25536
     * xhtml/svg:
     *   Adjust hierarchy of XQuery output to match mockup markup
     *
     * drag and drop based on http://dl.dropboxusercontent.com/u/169269/group_drag.svg
     * see also http://www.codedread.com/dragtest2.svg
     */
    window.addEventListener('load', plectogram_init, false);
    var djb = function () {
        return {
            adjustLinesAndColumns: function (how) {
                // only values should be 'fade' and 'restore'
                var lines = document.getElementsByTagName('line');
                var lineColor = how == 'fade' ? 'lightgray' : 'black';
                var columnOpacity = how == 'fade' ? .5 : 1;
                for (var i = 0; i < lines.length; i++) {
                    lines[i].style.stroke = lineColor;
                }
                var columns = document.getElementsByClassName('draggable');
                for (i = 0; i < djb.columnCount; i++) {
                    if (columns[i] !== djb.newG) {
                        columns[i].style.opacity = columnOpacity;
                    }
                }
            },
            assignEventListeners: function (g) {
                // other listeners are on window, so that they can be trapped when the mouse races ahead
                g.getElementsByTagName('image')[0].addEventListener('mousedown', djb.startMove, false);
                window.addEventListener('mousemove', djb.moveIt, false);
                window.addEventListener('mouseup', djb.endMove, false);
            },
            buildDict: function (g) {
                // attach a dictionary to each <g> with text as key and vertical position as value
                // also adds .getXPos() and .setXPos() methods to <g> object
                g.contents = {};
                var columnCells = g.getElementsByTagName('g');
                for (var i = 0; i < columnCells.length; i++) {
                    var cellText = columnCells[i].getElementsByTagName('text')[0].textContent;
                    var cellYPos = columnCells[i].getElementsByTagName('rect')[0].getAttribute('y');
                    g.contents[cellText] = cellYPos;
                }
                g.getXPos = function () {
                    return parseInt(this.getAttribute('transform').slice(10, -1));
                };
                g.setXPos = function (X) {
                    this.setAttribute('transform', 'translate(' + X + ')');
                };
            },
            createNewG: function (image) {
                djb.newG = image.parentNode.cloneNode(true);
                djb.buildDict(djb.newG);
                djb.newG.oldX = djb.newG.getXPos();
                image.parentNode.parentNode.appendChild(djb.newG);
                image.parentNode.parentNode.removeChild(image.parentNode);
            },
            drawLines: function () {
                var columns, key, x1, y1, x2, y2, linesG, newLine, i;
                columns = djb.htmlToArray(document.getElementsByClassName('draggable'));
                columns.sort(function (a, b) {
                    var result = a.getXPos() - b.getXPos();
                    return (result);
                }); // numerical sort by Xpos of column
                linesG = document.getElementById('lines');
                for (i = 1; i < djb.columnCount; i++) {
                    x1 = columns[i].getXPos();
                    x2 = columns[i - 1].getXPos() + djb.columnWidth;
                    for (key in columns[i].contents) {
                        if (columns[i].contents.hasOwnProperty(key)) {
                            if (columns[i - 1].contents.hasOwnProperty(key)) {
                                y1 = parseInt(columns[i].contents[key]) + djb.columnMidHeight;
                                y2 = parseInt(columns[i - 1].contents[key]) + djb.columnMidHeight;
                                newLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
                                newLine.setAttribute('x1', x1);
                                newLine.setAttribute('y1', y1);
                                newLine.setAttribute('x2', x2);
                                newLine.setAttribute('y2', y2);
                                newLine.setAttribute('stroke', 'darkgray');
                                newLine.setAttribute('stroke-width', '2');
                                linesG.appendChild(newLine);
                            }
                        }
                    }
                }
                djb.newG.oldX = djb.newG.getXPos();
            },
            endMove: function (evt) {
                evt.preventDefault();
                var i;
                window.removeEventListener('mousemove', djb.moveIt, false);
                window.removeEventListener('mouseup', djb.endMove, false);
                var columns = document.getElementsByClassName('draggable');
                var allNewColumnPositions = [];
                for (i = 0; i < djb.columnCount; i++) {
                    allNewColumnPositions.push(columns[i].getXPos());
                }
                // numerical array sorting at http://www.w3schools.com/jsref/jsref_sort.asp
                for (i = 0; i < djb.columnCount; i++) {
                    if (allNewColumnPositions.indexOf(djb.initialColumnPositions[i]) == -1) {
                        djb.newG.setXPos(djb.initialColumnPositions[i]);
                    }
                }
                djb.eraseLines();
                djb.drawLines();
                djb.adjustLinesAndColumns('restore');
                djb.newG = null;
            },
            eraseLines: function () {
                // someday this will be easier: http://red-team-design.com/removing-an-element-with-plain-javascript-remove-method/
                // must work backwards: http://stackoverflow.com/questions/1457544/javascript-loop-only-applying-to-every-other-element
                var lines = document.getElementsByTagName('line');
                for (var i = lines.length - 1; i >= 0; i--) {
                    lines[i].parentNode.removeChild(lines[i]);
                }
            },
            htmlToArray: function (htmlCollection) {
                return Array.prototype.slice.call(htmlCollection);
            },
            moveIt: function (evt) {
                evt.preventDefault();
                var oldObjectX = djb.newG.getXPos();
                var newObjectX = oldObjectX + evt.clientX - djb.mouseStartX;
                djb.newG.setXPos(newObjectX);
                djb.stretchLines();
                djb.mouseStartX = evt.clientX;
                if (newObjectX < djb.objectX - djb.spacing && newObjectX > '0') {
                    djb.swapColumns('left');
                } else if (newObjectX > djb.objectX + djb.spacing && djb.objectX < djb.farRight) {
                    djb.swapColumns('right');
                }
            },
            setXPos: function (g, Xpos) {
                g.setAttribute('transform', 'translate(' + Xpos + ')');
            },
            startMove: function (evt) {
                // global variable, used by djb.moveIt()
                djb.mouseStartX = evt.clientX;
                /*
                 * clone clicked <g> into djb.newG and attach event listeners
                 * svg has no concept of z height, so replace with clone at end of element sequence to avoid
                 *   losing focus by becoming hidden by later elements
                 */
                djb.createNewG(this); //image element that was clicked is the hook
                // djb.objectX is global, records starting position of drag
                djb.objectX = djb.newG.getXPos();
                djb.assignEventListeners(djb.newG);
                djb.adjustLinesAndColumns('fade');
            },
            stretchLines: function () {
                var newGX = djb.newG.getXPos();
                var lines = document.getElementsByTagName('line');
                for (var i = 0; i < lines.length; i++) {
                    var x1 = lines[i].getAttribute('x1');
                    var x2 = lines[i].getAttribute('x2');
                    if (x1 == djb.objectX + djb.spacing || x1 == djb.newG.oldX + djb.spacing) {
                        lines[i].setAttribute('x2', newGX + djb.columnWidth);
                    } else if (x2 == djb.objectX - djb.spacing + djb.columnWidth || x2 == (djb.newG.oldX - djb.spacing + djb.columnWidth)) {
                        lines[i].setAttribute('x1', newGX);
                    }
                }
            },
            swapColumns: function (side) {
                var columns, neighbor;
                columns = djb.htmlToArray(document.getElementsByClassName('draggable'));
                columns.sort(function (a, b) {
                    var result = a.getXPos() - b.getXPos();
                    return result;
                }); // numerical sort by Xpos of column
                neighbor = columns[djb.objectX / djb.spacing - 1];
                if (side == 'left') {
                    djb.objectX = djb.objectX - djb.spacing;
                    neighbor.setXPos(neighbor.getXPos() + djb.spacing);
                } else {
                    djb.objectX = djb.objectX + djb.spacing;
                    neighbor.setXPos(neighbor.getXPos() - djb.spacing);
                }
                djb.eraseLines();
                djb.drawLines();
            },
            columnsHTML: document.getElementsByClassName('draggable'),
            dummy: null
        }
    }();
    function plectogram_init() {
        var images, i;
        djb.columns = djb.htmlToArray(djb.columnsHTML);
        for (i = 0; i < djb.columns.length; i++) {
            djb.buildDict(djb.columns[i]);
        }
        djb.columnCount = djb.columns.length;
        djb.columnHeight = parseInt(djb.columns[0].getElementsByTagName('g')[0].getElementsByTagName('rect')[0].getAttribute('height'));
        djb.columnMidHeight = djb.columnHeight / 2;
        djb.columnWidth = parseInt(djb.columns[0].getElementsByTagName('g')[0].getElementsByTagName('rect')[0].getAttribute('width'));
        djb.initialColumnPositions = [];
        for (i = 0; i < djb.columns.length; i++) {
            djb.initialColumnPositions.push(djb.columns[i].getXPos());
        }
        djb.spacing = djb.initialColumnPositions[1] - djb.initialColumnPositions[0];
        djb.farRight = djb.initialColumnPositions[djb.initialColumnPositions.length - 1];
        djb.newG = null;
        djb.mouseStartX = null;
        images = document.getElementsByTagName('image');
        for (i = 0; i < images.length; i++) {
            images[i].addEventListener('mousedown', djb.startMove, false);
        }
    }
    
    “严格使用”;
    /*
    *简介:对Plectogram的拖放支持
    *开发商:David J.Birnbaum,djbpitt@gmail.com http://www.obdurodon.org
    *项目:http://repertorium.obdurodon.org
    *日期:第一版2014-05-30
    *最后修订日期:2014-06-22
    *
    *要做:
    *js:
    *将自动执行匿名函数(SEF)中的初始化包装到
    *绕过显式寻址窗口“加载”事件:
    *     http://code.tutsplus.com/tutorials/key-principles-of-maintainable-javascript--net-25536
    *xhtml/svg:
    *调整XQuery输出的层次结构以匹配实体模型标记
    *
    *基于http://dl.dropboxusercontent.com/u/169269/group_drag.svg
    *另见http://www.codedread.com/dragtest2.svg
    */
    addEventListener('load',plectogram_init,false);
    var djb=函数(){
    返回{
    调整行和列:功能(如何){
    //只有值应为“淡入”和“恢复”
    var lines=document.getElementsByTagName('line');
    var lineColor=how=='fade'?'lightgray':'black';
    var columnOpacity=how=='fade'?.5:1;
    对于(变量i=0;i