使用JavaScript通过鼠标拖动排序图形列表

时间:2014-02-24 03:49:07

标签: javascript jquery css

注意:问题的确切描述遵循以下CSS。示例代码可以在this小提琴中看到。

我有一个父div,其中有一个列表的子div,如下所示:

List of children divs

所述容器和子容器的

HTML 是:

<div class="categories_container">
    <div class="category one">One</div>
    <div class="category two">Two</div>
    <div class="category three">Three</div>
    <div class="category four">Four</div>
    <div class="category five">Five</div>
    <div class="category six">Six</div>
</div>

班级.one.two.three等......在列表中的相对位置。

子元素在其父元素中以绝对定位定位。

CSS 如下(为简单起见未显示某些属性):

.categories_container {
    height: 324px;
    width: 100%;
    position: relative;
}
.category {
    height: 50px;
    width: 98%;
    position: absolute;
    left: 0px;
    z-index: 0;
}
.one {
    top: 0px;
}
.two {
    top: 54px;
}
.three {
    top: 108px;
}
.four {
    top: 162px;
}
.five {
    top: 216px;
}
.six {
    top: 270px;
}

this 小提琴中可以看到,您可以在任何一个子元素上单击(并按住),然后在父div中上下移动它。释放鼠标时,所选子项将快速回到原始位置。

问题:

如何检测所选元素是否已被拖到另一个元素之上?我不仅想知道它们是否重叠,而且想在其上放置一个范围。有点像...

if(center of current child is overtop a set range within another child){
    do stuff...
}

我现在想要做的(作为概念证明)是让孩子的背景颜色改变 WHILE 所选孩子的垂直中心在0.4-0.6范围内底部孩子的身高。如果将所选子项拖出所述区域,则背景应更改回来。

我尝试过类似的事情:

$('.category').mouseover(function(){
    if(dragging){
        ... execute code...
    }
});

但似乎如果我将一个元素拖到另一个元素上,则底部元素无法看到鼠标,因此该函数永远不会被执行。

同时

我在拖动时尝试了一些不同的方法将光标保持为pointer,但无论它在拖动时切换到文本光标是什么。所以对此的任何帮助也将受到赞赏。

对于指针,我尝试将$(this).css('cursor', 'pointer');放在mousedownmouse move函数中,但无济于事。

提前致谢!对不起,如果其中任何一个令人困惑。

1 个答案:

答案 0 :(得分:3)

Here 是我提出的解决方案,纯粹使用JS和JQuery,不需要外部库,也不使用JQueryUI Sortables。

<强> HTML:

<div class="list_container">
    <div class="list_item">One</div>
    <div class="list_item">Two</div>
    <div class="list_item">Three</div>
    <div class="list_item">Four</div>
    <div class="list_item">Five</div>
    <div class="list_item">Six</div>
</div>

其中list_container包含各个list_item元素。它是两者中的后者,可以移动来创建您的排序列表。你可以在list_item内放置任何你想要的东西,它仍然可以正常工作。

<强> CSS:

.list_container {
    position: relative;
}
.list_item {
    position: absolute;
    z-index: 0;
    left: 0px;
}
.list_item.selected {
    z-index: 1000;
}

请访问this小提琴,了解完整的CSS规则列表(上面只显示了必要的规则)。

<强> JavaScript的:

我将逐个查看,然后在底部显示完整的代码。

首先,我定义了一个匹配索引号的数组和它们的书面对应物

var classes = new Array("one", "two", "three", ...);

这用于动态创建类(在页面加载时)。这些类用于排序列表。您只需要使用列表中的项目填充此数组。这是我编写的代码的一个缺点,我不确定如何克服这个问题(输入数百个项目列表的元素非常繁琐,或者更多!)

接下来,还有一些其他变量:

var margin = 2;       // Space desired between each list item
var $el;              // Used to hold the ID of the element that has been selected
var oldPos = 0;       // The position of the selected element BEFORE animation
var newPos = 0;       // The position of the selected element AFTER animation (also current position)
var dragging = false; // Whether or not an item is being moved

var numElements = $('.list_container > div').length;

// selectionHeight is the height of each list element (assuming all the same height)
// It includes the div height, the height of top and bottom borders, and the desired margin

var selectionHeight = $('.list_container .list_item').height() + parseInt($('.list_container .list_item').css("border-bottom-width")) + parseInt($('.list_container .list_item').css("border-top-width")) + margin;

var classInfo = '';  // classInfo will be populated with the information that is used to dynamically create classes upon page load

当页面加载时,遍历每个list_item并根据列表中的初始位置为其分配一个类。同时将classInfo添加到列表项TOP的位置。

$('.list_container .list_item').each(function (index) {
    $(this).addClass(classes[index]);
    classInfo += '.' + classes[index] + ' {top: ' + index * selectionHeight + 'px;}\n';
});

现在,使用上面创建的classInfo,动态地将类写入页面。

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = classInfo;
document.getElementsByTagName('head')[0].appendChild(style);

上面的代码将把所需的类写入页面的HTML中。如果您查看页面的来源,则可以在页面的头部看到这些类。

现在订购部分。首先,mousedown

$('.list_item').mousedown(function (ev) {
    $el = $(this);
    oldPos = $el.index() + 1;
    newPos = oldPos;
    dragging = true;
    startY = ev.clientY;               // Gets the current mouse position
    startT = parseInt($el.css('top')); // Gets the current position of the TOP of the item
    $el.addClass('selected');          // Adding class brings it to top (z-index) and changes color of list item
});

接下来,mousemovemouseup功能捆绑在一起

$(window).mousemove(function (ev) {  // Use $(window) so mouse can leave parent div and still work
    if (dragging) {
        $el.attr('class', 'list_item')  // Remove the numbered class (.one, .two, etc)
        $el.addClass('selected');       // Add this class back for aesthetics

        // ----- calculate new top
        var newTop = startT + (ev.clientY - startY);
        $el.css('cursor', 'pointer');
        // ------

        //------ stay in parent
        var maxTop = $el.parent().height() - $el.height();
        newTop = newTop < 0 ? 0 : newTop > maxTop ? maxTop : newTop;
        $el.css('top', newTop);
        //------

        newPos = getPos(newTop, selectionHeight); // Determine what the current position of the selected list item is

        // If the position of the list item has changed, move the position's current element out of the way and reassign oldPos to newPos
        if (oldPos != newPos) {
            moveThings(oldPos, newPos, selectionHeight);
            oldPos = newPos;
        }
    }
}).mouseup(function () {
    dragging = false;            // User is no longer dragging
    $el.removeClass('selected'); // Element is no longer selected
    setNewClass($el, newPos);    // Set the new class of the moved list item
    $el.css('top', (newPos - 1) * selectionHeight);  // Position the moved element where it belongs. Otherwise it'll come to rest where you release it, not in its correct position.
});

最后,getPosmoveThingssetNewClass这三个函数如下:

function getPos(a, b) { // a == newTop, b == selectionHeight
return Math.round( (a/b) + 1 ); 
}

getPos通过找出所选元素当前所在的区域来工作。如果newTop小于.5b,则它在区域1中。如果在.5b和1.5b之间,则它是区域2。如果在1.5b和2.5b之间,那么在区域3中。依此类推。在一张纸上写下几个案例,它会发生什么事情。

function moveThings(a, b, c) { // a == oldPos, b == newPos, c == selectedHeight
    var first = classes[b - 1];  // What is the current class of the item that will be moved
    var $newEl = $('.list_container .' + first);  // ID of element that will be moved

    if (a < b) { // oldPos less than newPos
        var second = classes[b - 2]; // The new class of the moved element will be one less
        var newTop = parseInt($newEl.css('top')) - c; // Top of element will move up
    } else { // oldPos more than newPos
        var second = classes[b]; // The new class of the moved element will be one more
        var newTop = parseInt($newEl.css('top')) + c; // Top of element will move down
    }

    // The following line of code is required, otherwise the following animation 
    // will animate of from top=0px to the new position (opposed to from top=currentPosition)
    // Try taking it out and seeing
    $newEl.css('top', parseInt($newEl.css('top')));
    $newEl.removeClass(first); // Remove the current numbered class of element to move
    // Move element and remove the added style tags (or future animations will get buggy)
    $newEl.animate({top: newTop}, 300, function () {
        $newEl.removeAttr('style');
    });
    $newEl.addClass(second); // Add the new numbered class

    return false; // Cleans up animations
}

上面的函数是实际的动画部分,并移动列表项以适应所选的列表项。

function setNewClass(e, a) { // e == selected element, a == newPos
    // Remove 'selected' class, then add back the 'list_item' class and the new numbered class
    e.attr('class', 'list_item').addClass(classes[a-1]);
}

**所有JavaScript在一起:**

var classes = new Array("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeem", "eighteen", "nineteen", "twenty", "twentyone", "twentytwo", "twentythree", "twentyfour");

$(document).ready(function () {
    var margin = 2;
    var $el;
    var oldPos = 0;
    var newPos = 0;
    var dragging = false;

    var selectionHeight = $('.list_container .list_item').height() + parseInt($('.list_container .list_item').css("border-bottom-width")) + parseInt($('.list_container .list_item').css("border-top-width")) + margin;

    var classInfo = '';

    $('.list_container .list_item').each(function (index) {
        $(this).addClass(classes[index]);
        classInfo += '.' + classes[index] + ' {top: ' + index * selectionHeight + 'px;}\n';
    });

    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = classInfo;
    document.getElementsByTagName('head')[0].appendChild(style);

    $('.list_item').mousedown(function (ev) {
        $el = $(this);
        oldPos = $el.index() + 1;
        newPos = oldPos;
        dragging = true;
        startY = ev.clientY;
        startT = parseInt($el.css('top'));
        $el.addClass('selected');
    });

    $(window).mousemove(function (ev) {
        if (dragging) {
            $el.attr('class', 'list_item')
            $el.addClass('selected');

            // ----- calculate new top
            var newTop = startT + (ev.clientY - startY);
            $el.css('cursor', 'pointer');
            // ------

            //------ stay in parent
            var maxTop = $el.parent().height() - $el.height();
            newTop = newTop < 0 ? 0 : newTop > maxTop ? maxTop : newTop;
            $el.css('top', newTop);
            //------

            newPos = getPos(newTop, selectionHeight);

            if (oldPos != newPos) {
                moveThings(oldPos, newPos, selectionHeight);
                oldPos = newPos;
            }
        }
    }).mouseup(function () {
        dragging = false;
        $el.removeClass('selected');
        setNewClass($el, newPos);
        $el.css('top', (newPos - 1) * selectionHeight);
    });
});

function getPos(a, b) { // a == topPos, b == selectionHeight
    return Math.round((a / b) + 1);
}

function moveThings(a, b, c) { // a == oldPos, b == newPos, c == selectedHeight
    var first = classes[b - 1];
    var $newEl = $('.list_container .' + first);

    if (a < b) { // oldPos less than newPos
        var second = classes[b - 2];
        var newTop = parseInt($newEl.css('top')) - c;
    } else { // oldPos more than newPos
        var second = classes[b];
        var newTop = parseInt($newEl.css('top')) + c;
    }

    $newEl.css('top', parseInt($newEl.css('top')));
    $newEl.removeClass(first);
    $newEl.animate({
        top: newTop
    }, 300, function () {
        $newEl.removeAttr('style');
    });
    $newEl.addClass(second);

    return false; // Cleans up animations
}

function setNewClass(e, a) { // e == selected element, a == newPos
    e.attr('class', 'list_item').addClass(classes[a - 1]);
}