Web前端JavaScript笔记(5)事件-拖拽

阻止默认行为和超链接

在浏览器上运行网页后,右击会弹出菜单,这属于浏览器的默认行为。如何禁止这一默认行为:

<script>
        window.onload = function () {
            document.oncontextmenu = function () {
                return false
            }
        }
</script>

对于a链接,如果点击a链接后,会默认直接进行跳转,如何组织这一默认行为,在点击a连接后,想用户进行询问之后,在进行是否跳转。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let tag_a = $("baidu");
            tag_a.onclick = function () {
                return confirm("Quit?");   // confirm会返回true和false
            };
        }
    </script>
</head>
<body>
    <a id="baidu" href="https://ww.baidu.com" target="_blank">百度</a>
</body>
</html>

另一种方法,阻止超链接默认行为的方法,由系统提供的属性和方法实现:

1. preventDefault();      W3C, 阻止默认行为

2. window.event.returnValue = false;    // IE浏览器,阻止默认行为

在实际应用中,需要自定义个能兼容不同浏览器的版本:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let tag_a = $("baidu");
            tag_a.onclick = function (ev) {
                let e = ev || window.event;
                preventDefaultAction(e);
                alert("后续操作");
            };
        };
        
        // 跨浏览器阻止默认行为
        function preventDefaultAction(e) {
            if(e.preventDefault)
            {
                e.preventDefault();
            }
            else
            {
                window.event.returnValue = false;
            }
        }
    </script>
</head>
<body>
    <a id="baidu" href="https://ww.baidu.com" target="_blank">百度</a>
</body>
</html>

拖拽实现:

1. mousedown     

记录鼠标按下时鼠标相对于被拖拽的物体的位置:

 offsetX =  e.clientX - offsetX

 offsetY = e.clientY - offsetY

2. mousemove     

在鼠标一定的过程中,需要一致保持被拖拽物体与鼠标的相对距离

3. mouseup

取消拖拽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let tag_div = $("box");
            let absolute_x = 0;
            let absolute_y = 0;
            let current_x = 0;
            let current_y = 0;

            tag_div.onmousedown = function (ev) {
                let e = ev || window.event;
                absolute_x = e.clientX - tag_div.offsetLeft;
                absolute_y = e.clientY - tag_div.offsetTop;

                // 绑定鼠标移动事件 document上
                document.onmousemove = function (ev) {
                    let e = ev || window.event;
                    current_x = e.clientX;
                    current_y = e.clientY;
                    tag_div.style.left = current_x - absolute_x + "px";
                    tag_div.style.top = current_y - absolute_y + "px";
                }
            };

            window.onmouseup = function () {
                document.onmousemove = null;
            };

        }
    </script>
    <style>
        #box{
            width: 200px;
            height: 100px;
            border: 1px solid black;
            background: #ffcf3f;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="box"></div>
</body>
</html>

对拖拽的改进,使得被推拽的物体不能超过屏幕的范围:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let tag_div = $("box");
            let absolute_x = 0;
            let absolute_y = 0;
            let current_x = 0;
            let current_y = 0;

            tag_div.onmousedown = function (ev) {
                let e = ev || window.event;
                absolute_x = e.clientX - tag_div.offsetLeft;
                absolute_y = e.clientY - tag_div.offsetTop;
                // 获取窗口的大小
                let window_width = document.documentElement.clientWidth || document.body.clientWidth;
                let window_height = document.documentElement.clientHeight || document.body
                // 绑定鼠标移动事件 document上
                document.onmousemove = function (ev) {
                    let e = ev || window.event;
                    current_x = e.clientX;
                    current_y = e.clientY;
                    let adjust_x = current_x - absolute_x;
                    let adjust_y = current_y - absolute_y;
                    if(adjust_x < 0)
                    {
                        adjust_x = 0;
                    }
                    if(adjust_x >= window_width - tag_div.offsetWidth)
                    {
                        adjust_x = window_width - tag_div.offsetWidth;
                    }
                    if(adjust_y < 0)
                    {
                        adjust_y = 0;
                    }
                    if(adjust_y >= window_height - tag_div.offsetHeight)
                    {
                        adjust_y = window_height - tag_div.offsetHeight;
                    }
                    tag_div.style.left = adjust_x + "px";
                    tag_div.style.top = adjust_y + "px";
                };
            };

            window.onmouseup = function () {
                document.onmousemove = null;
            }

        }
    </script>
    <style>
        #box{
            width: 200px;
            height: 100px;
            border: 1px solid black;
            background: #ffcf3f;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="box"></div>
</body>
</html>

可以对上述的实现拖拽代码进行封装,成为一个独立的函数,放到too.js中可供使用

事件委托:

例如,对于如下的结构:

<body>
    <ul id="list">
        <li>11</li>
        <li>22</li>
        <li>33</li>
        <li>44</li>
    </ul>
</body>

需要给<li>标签添加相同的点击事件,按照之前的做法,是先获得所有的<li>标签,再通过循环为每一个<li>标签添加点击事件。如果需要在添加一个按钮,每次点击按钮后,都需要新增一个<li>标签,则新增的<li>标签是没有绑定点击事件的。

这样做的缺点是:

1. for循环添加相同的事件函数,浪费资源

2. 不能解决新增的节点事件绑定问题

解决方法:事件委托

A委托---->B代理,B执行动作,A受益

1. 找到当前节点的父节点或者祖先节点

2. 将事件添加到父节点或者祖先节点

3. 找到触发对象,判断触发对象是否满足条件,如果是执行后续操作

所以可以通过上述的操作,实现<li>标签委托<ul>,此时只需要为<ul>标签添加事件函数,同时新增的<li>节点也可以响应点击事件,这个事件是<li>委托给ul的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let btn = $("add");
            let tag_ul = $("list");   // 找到li的父节点
            tag_ul.onclick = function (ev) {
                let e = ev || window.event;
                let target_e = e.target || window.event.srcElement;  // 获取事件对象
                if(target_e.nodeName.toLowerCase() === "li")   // 判断触发对象是否满足条件
                {
                    target_e.style.background = "red";
                }
            };
            let cnt = 1;    // 增加新节点
            btn.onclick = function () {
                let newLi = document.createElement("li");
                newLi.innerHTML = (cnt++ * 1111).toString();
                tag_ul.appendChild(newLi);
            }
        }
    </script>
    <style>

    </style>
</head>
<body>
    <button id="add">增加</button>
    <ul id="list">

    </ul>
</body>
</html>

可以利用事件委托对前面的增加记录案例进行改进:
这样只需要添加一个点击就可以实现所有按钮绑定事件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let input_tag = $("input_text");
            let bottom_section = $("bottom");
            $("add").onclick = function ()
            {
                let input_content = input_tag.value;
                if(!input_content){
                    alert("请输入内容");
                }
                else{
                    // 开始创建节点
                    let node = document.createElement("div");
                    let text_node = document.createTextNode(input_content);
                    let btn_node = document.createElement("button");
                    let btn_text_node = document.createTextNode("x");
                    btn_node.appendChild(btn_text_node);
                    node.appendChild(text_node);
                    node.appendChild(btn_node);
                    node.style.background = randomColor();
                    bottom_section.appendChild(node);
                    input_tag.value = "";   // 清空输入框
                    // updateItemsButtons();
                }
            };

            $("delete").onclick = function () {
                let last_node = bottom_section.lastChild;
                bottom_section.removeChild(last_node);
                // updateItemsButtons();
            };

            $("clone").onclick = function () {
                let last_node = bottom_section.lastChild;
                let clone_node = last_node.cloneNode(true);   // 深拷贝
                bottom_section.appendChild(clone_node);
                // updateItemsButtons();
            };
            
            // 事件委托
            bottom_section.onclick = function (ev) {
                let e = ev || window.event;
                let target = e.target || window.event.srcElement;   // 找到触发对象
                if(target.nodeName.toLowerCase() === "button")   // 要转换成小写
                {
                    bottom_section.removeChild(target.parentNode);   // 删除当前对象的父节点
                }
            };
            
            input_tag.onkeydown = function (ev)
            {
                // 给输入框添加快捷键
                let e = ev || window.event;
                let key_code = e.keyCode || e.which;   // 判断回车键
                if(e.ctrlKey && key_code===13)
                {
                    $("add").onclick();
                }
            }
        }

    </script>
    <style>
        #container{
            width: 200px;
            border: 1px solid black;
        }
        #top{
            width: 100%;
            height: 30px;
        }
        #top input{
            height: 20px;
            line-height: 30px;
            margin: 0 auto;
        }
        #middle{
            width: 100%;
            height: 50px;
            border: none;
            display: flex;
            flex-direction: row;
            justify-content: space-around;
            background: #0f6674;
        }
        #middle button{
            /*width: 25%;*/
            height: 50%;
            align-self: center;
            /*margin-top: 10px;*/
        }

        #bottom{

        }

        #bottom div{
            height: 25px;
            position: relative;
        }
        #bottom div button{
            position: absolute;
            top: 1px;
            right: 2px;
        }
    </style>
</head>
<body>
    <div id="container">
        <div id="top">
            <input type="text" id="input_text" placeholder="请输入内容">
        </div>
        <div id="middle">
            <button id="add">添加</button>
            <button id="delete">删除</button>
            <button id="clone">克隆</button>
        </div>
        <div id="bottom">
            <!--<div>content<button>x</button></div>-->
        </div>
    </div>
</body>
</html>

事件监听器:

传统的事件绑定,需要先获取到相应的节点,再去绑定事件,但是在一些场景中,这种传统的方法不能够满足需求:
1. 例如,对一个按钮重复绑定同一个是按,当事件被触发的时候,前面的事件会被后面的事件覆盖

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let btn = $("add");
            btn.onclick = function () {
                alert("点击1");
            };
            btn.onclick = function () {
                alert("点击2");
            };
        }
    </script>
    <style>

    </style>
</head>
<body>
    <button id="add">增加</button>
</body>
</html>

当点击按钮后只能出现"点击2","点击1"会被覆盖掉。

而事件监听器是另一种绑定事件的方式,addEventListener()用于向指定元素添加事件句柄,IE8以下不支持,火狐和谷歌支持/

removeEventListener()删除事件监听器。

addEventListener("click", functionName, bool):

第一个参数表示需要监听的操作,例如click

第二个参数表示绑定的事件函数,函数名或者匿名函数

第三个参数表示 true: 事件捕获,false事件冒泡, 默认/    事件捕获:与事件冒泡案例中的顺序正好相反。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let btn = $("add");
            btn.addEventListener("click", function () {
                alert("点击1");
            }, false);
            btn.addEventListener("click", function () {
                alert("点击2");
            }, false);
        }
    </script>
    <style>

    </style>
</head>
<body>
    <button id="add">增加</button>
</body>
</html>

而利用事件监听器就可以执行所有绑定的事件。利用事件监听器,可以给标签绑定多个事件函数,同时,也能够精确的去删除标签上绑定的某一个事件函数,但是传统事件绑定方法,一是会后绑定的事件函数会覆盖掉前面的事件函数,在一个是,如果需要删除事件函数,则会将所有的事件函数都删除。(node.onclick = null)

低版本浏览器需要兼容事件监听器:

1. attachEvent()

2. detachEvent()

function addEvent(node, eventType, funcName) {
    if (node.addEventListener)   // 判断是否支持
    {
        node.addEventListener(eventType, funcName, false);
    }
    else {
        node.attachEvent("on" + eventType, funcName);
    }
}

function removeEvent(node, eventType, funcName) {
    if(node.removeEventListener)
    {
        node.removeEventListener(eventType, funcName);
    }
    else
    {
        node.detachEvent("on" + eventType, funcName);
    }
}

案例:动态生成表格

输入表格的行数和列数,自动生成表格:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let btn = $("gen");
            let row = $("row");
            let col = $("col");
            let table = $("t1");
            btn.onclick = function () {
                let row_num = row.value;
                let col_num = col.value;
                if(!row_num || !col_num)
                {
                    alert("请输入完整的行列");
                }
                else
                {
                    let row_max = parseInt(row_num);
                    let col_max = parseInt(col_num);
                    if (isNaN(row_max) || isNaN(col_max))   //行列必须是数字
                    {
                        alert("行列必须是数字");
                    }
                    else
                    {
                        for(let row_cnt=0; row_cnt<row_max; row_cnt++)
                        {
                            // 控制行数
                            let tr = document.createElement("tr");  // 创建行
                            for (let col_cnt=0; col_cnt<col_max; col_cnt++)
                            {
                                let td = document.createElement("td");
                                tr.appendChild(td);
                            }
                            let td_del = document.createElement("td");
                            td_del.innerHTML = "<button>删除</button>";
                            tr.appendChild(td_del);
                            table.appendChild(tr);
                        }
                    }
                }

            };
            table.onclick = function (ev) {
                let e = ev || window.event;
                let target = e.target || window.event.srcElement;
                if(target.nodeName.toLowerCase() === "button")
                {
                    table.removeChild(target.parentNode.parentNode);
                }
            };

        }
    </script>
    <style>
        #t1 tr td{
            width: 100px;
            height: 30px;
            border: 1px solid black;
        }

        #t1 tr td:last-child button{
            display: block;
            margin: 0 auto;
        }

        #t1{
            border: 1px solid black;
            border-collapse: collapse;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <input type="text" placeholder="行数" id="row">
    <input type="text" placeholder="列数" id="col">
    <button id="gen">生成</button>
    <table id="t1">

    </table>
</body>
</html>

效果如下图所示:

案例:放大镜

在图片中,随着鼠标的移动,图片的局部区域会在另一个地方被放大一定的倍数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script rel="script" src="../JavaScript/tool.js"></script>
    <script>
        window.onload = function () {
            let tag_mark = $("mark");
            let tag_sourcePic = $("source-pic");
            let tag_largePic = $("large-pic");
            let tag_img = tag_largePic.getElementsByTagName("img")[0];
            tag_sourcePic.onmouseover = function () {
                tag_mark.style.display = "block";
            };
            tag_sourcePic.onmouseout = function () {
                tag_mark.style.display = "none";
            };
            tag_sourcePic.onmousemove = function (ev) {
                let e = ev || window.event;
                let temp_x = e.clientX - tag_sourcePic.offsetLeft -tag_mark.offsetWidth /2;
                let temp_y = e.clientY - tag_sourcePic.offsetTop - tag_mark.offsetHeight/2;
                if (temp_x < 0){
                    temp_x = 0;
                }
                if (temp_x > tag_sourcePic.offsetWidth - tag_mark.offsetWidth)
                {
                    temp_x = tag_sourcePic.offsetWidth - tag_mark.offsetWidth;
                }
                if (temp_y < 0)
                {
                    temp_y = 0;
                }

                if (temp_y > tag_sourcePic.offsetHeight - tag_mark.offsetHeight)
                {
                    temp_y = tag_sourcePic.offsetHeight - tag_mark.offsetHeight;
                }
                tag_mark.style.left = temp_x + "px";
                tag_mark.style.top = temp_y + "px";

                // 放大效果
                tag_img.style.left = temp_x * -3.2 +"px";
                tag_img.style.top = temp_y * -3.2 + "px";
            }


        }
    </script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #source-pic{
            width: 336px;
            height: 200px;
            position: absolute;
            left: 20px;
            top: 25px;
        }
        #source-pic img{
            display: block;
            width: 100%;
            height: 100%;
        }

        #source-pic #mark{
            width: 50px;
            height: 50px;
            opacity: 0.5;
            background: white;
            position: absolute;
            left: 0;
            top: 0;
            display: none;
        }

        #large-pic{
            width: 200px;
            height: 200px;
            border: 1px solid black;
            position: absolute;
            left: 20px;
            top: 250px;
            overflow: hidden;
        }

        #large-pic img
        {
            position: absolute;
            left: 0;
            top: 0;
        }

    </style>
</head>
<body>
    <div id="source-pic">
        <img src="../picture/others/timg1.jpg" alt="">
        <div id="mark"></div>
    </div>
    <div id="large-pic">
        <img src="../picture/others/timg1.jpg" alt="">
    </div>
</body>
</html>

 

posted @ 2020-03-29 09:44  Alpha205  阅读(158)  评论(0编辑  收藏  举报