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

标签 javascript jquery css

注意:问题的准确描述遵循下面的 CSS。示例代码可见this fiddle 。

我有一个包含子 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中可以看出 fiddle,您可以单击(并按住)任何一个子元素并在父 div 中上下移动它。当您释放鼠标时,选定的子项会弹回其原始位置。

问题:

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

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

我现在想做的(作为概念证明)是让下面的 child 的背景颜色发生变化当所选 child 的垂直中心在 0.4-0.6 范围内时底部 child 的高度。如果选定的 child 被拖出所述区域,背景应该变回。

我试过类似的方法:

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

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

还有:

我尝试了几种不同的方法来在拖动时将光标保持为 pointer,但无论如何它在拖动时都会切换到文本光标。因此,我们也将不胜感激。

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

提前致谢!抱歉,如果其中任何一个令人困惑。

最佳答案

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 fiddle 获取完整的 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

$('.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 函数绑定(bind)在一起

$(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]);
}

关于javascript - 使用 JavaScript 通过鼠标拖动来排序图形列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21978551/

相关文章:

javascript - 隐藏部分文本 html、css、javascript

javascript - EaselJS:通过鼠标按下触发添加子项

javascript - 动态附加元素后重新应用样式属性

html - CSS 过渡错误

javascript - 将日期选择器值设置为空

javascript - 表单插件一次提交所有表单

jquery - 内容类型无法通过 AJAX Post 发送到服务器

javascript - 使用 JavaScript/JQuery 将 html 表格数据导出到 Excel 在 chrome 浏览器中无法正常工作

javascript - 如何使用矩比较一周中的天数

javascript - d3 复利债务