js动态生成元素 on()属性失效问题 (点击放大图片点击消失)

本文详细探讨了在jQuery中正确处理动态生成元素的点击事件,通过事件委托解决了弹出产品列表框上的图片点击放大功能失效的问题。介绍了bind(), live(), on()的区别,展示了如何使用on()方法委托事件给父元素,确保动态生成的元素也能响应事件。

情景:需要在已经弹出的产品列表框上1 ,再加一层点击放大弹出2。(见下图pop1, pop2)
 

失效js 

jQuery(".tm-m-photo-viewer ").on("click","img",function(){
    var _this = jQuery(this);//将当前的img元素作为_this传入函数
    imgShow("#outerdiv", "#innerdiv", "#bigimg", _this);
});

尝试一通bind(),live(),on() 区别之后还是失效, 而且on()是推荐使用的新的。

//bind    $( "#members li a" ).on( "click", function( e ) {} );   $( "#members li a" ).bind( "click", function( e ) {} );
 
// Live    $( document ).on( "click", "#members li a", function( e ) {} );    $( "#members li a" ).live( "click", function( e ) {} );
 
// Delegate   $( "#members" ).on( "click", "li a", function( e ) {} );    $( "#members" ).delegate( "li a", "click", function( e ) {} );

修改后js

jQuery(.catalog-product-popup").on("click",".tm-m-photo-viewer img",function(){
    var _this = jQuery(this);//将当前的img元素作为_this传入函数
    imgShow("#outerdiv", "#innerdiv", "#bigimg", _this);
});

catalog-product-popup 是最开始存在但不显示的pop部分。效果成功。

问题的关键:动态生成的元素要通过事件委托来委托给父元素。本例委托给了弹框1未出现就已存在的 catalog-product-popup。

附上点击放大图片单独页面

<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script type="text/javascript" src="https://www.obido.cn/shop/skin/frontend/default/theme286k/js/jquery-1.10.2.min.js"></script>
<style>
      .img-div img{ width:100px; height:100px; }
      #outerdiv{ position:fixed; top:0; left:0; background:rgba(0,0,0,0.7); z-index:2; width:100%; height:100%; display:none; }
      #innerdiv{ position:fixed; z-index:3; display:none;}
      /* 此处上面 position 是 absolute 时就是相对于弹出框口的位置 */
      #bigimg{ border:5px solid #fff; }
  </style>
</head>

/*附上HTML*/
 <body>
<div class="tm-m-photo-viewer">
        <div class="img-div" id="mytest">  
            <img src="https://www.obido.cn/test/shop/media/catalog/product/2/_/2.jpg" class="pimg">
        </div>
</div>
<div id="outerdiv"></div>
 <div id="innerdiv">
    <img id="bigimg" src="#">
</div>

/*完整JS*/
<script>
    jQuery("body").on("click",".tm-m-photo-viewer img",function(){
        var _this = jQuery(this);//将当前的img元素作为_this传入函数
        imgShow("#outerdiv", "#innerdiv", "#bigimg", _this);
    });
    function imgShow(outerdiv, innerdiv, bigimg, _this){
        var src = _this.attr("src");//获取当前点击的pimg元素中的src属性  
        jQuery(bigimg).attr("src", src);//设置#bigimg元素的src属性
            /*获取当前点击图片的真实大小,并显示弹出层及大图*/
        jQuery("<img/>").attr("src", src).load(function(){
            var windowW = jQuery(window).width();//获取当前窗口宽度
            var windowH = jQuery(window).height();//获取当前窗口高度
            var realWidth = this.width;//获取图片真实宽度  
            var realHeight = this.height;//获取图片真实高度
            var imgWidth, imgHeight;  
            var scale = 0.8;//缩放尺寸,当图片真实宽度和高度大于窗口宽度和高度时进行缩放  

            alert(windowH);
            alert(realHeight);

            if(realHeight>windowH*scale) {//判断图片高度
                imgHeight = windowH*scale;//如大于窗口高度,图片高度进行缩放
                imgWidth = imgHeight/realHeight*realWidth;//等比例缩放宽度  
                if(imgWidth>windowW*scale) {//如宽度扔大于窗口宽度  
                    imgWidth = windowW*scale;//再对宽度进行缩放  
                }  
            } else if(realWidth>windowW*scale) {//如图片高度合适,判断图片宽度  
                imgWidth = windowW*scale;//如大于窗口宽度,图片宽度进行缩放  
                            imgHeight = imgWidth/realWidth*realHeight;//等比例缩放高度  
            } else {//如果图片真实高度和宽度都符合要求,高宽不变  
                imgWidth = realWidth;  
                imgHeight = realHeight;  
            }  
            jQuery(bigimg).css("width",imgWidth);//以最终的宽度对图片缩放
            var w = (windowW-imgWidth)/2;//计算图片与窗口左边距  
            var h = (windowH-imgHeight)/2;//计算图片与窗口上边距
            jQuery(innerdiv).css("display","block");
            jQuery(innerdiv).css({"top":h, "left":w});//设置#innerdiv的top和left属性
            jQuery(outerdiv).fadeIn("fast");//淡入显示#outerdiv及.pimg
        });  
          
        jQuery(outerdiv).click(function(){//再次点击淡出消失弹出层
            jQuery(this).fadeOut("fast");
            jQuery(innerdiv).css("display","none");
        });  
    }
</script>
</body>
</html

 

如果你发现上面生成的代码 **“拖拽无效”或“拽不动”**,这是一个非常常见的问题。虽然代码逻辑正确,但在实际运行中可能因为以下几个关键原因导致 **拖拽无法触发或响应**。 --- ## 🔍 常见原因分析与解决方案 ### ✅ 1. `e.target.dataIndex` 为 `undefined` —— 没有正确识别点击的数据点 这是最常见的问题! #### ❌ 问题原因: ECharts 中只有当鼠标点击的是 **symbol(数据点图形)本身** 时,`e.target` 才会携带 `dataIndex`。 但如果点击的是折线空白区域、label 或其他非 symbol 元素,`e.target` 就不是我们想要的对象。 更重要的是:**默认情况下,ECharts 不会给每个 symbol 设置 `name` 属性为 `'item'`**,所以这行判断会失败: ```js if (el instanceof echarts.Element && el.name === 'item') ``` 而实际上 `el.name` 可能是空或者未定义。 --- ### ✅ 解决方案:使用更可靠的判定方式 修改 `mousedown` 判断逻辑,改为通过 `seriesIndex` 和 `dataIndex` 是否存在来判断是否点中了数据点: #### ✅ 修改后的 `mousedown` 监听代码: ```js myChart.getZr().on('mousedown', function (e) { // 正确判断是否点击到了数据点 if (e.target && e.target.dataIndex != null && e.target.seriesIndex != null) { draggingIndex = e.target.dataIndex; myChart.getZr().setCursorStyle('grabbing'); } }); ``` > ✅ 这样只要点击的是图表中的 symbol(圆点),就能正确捕获 `dataIndex`。 --- ### ✅ 2. `convertFromPixel` 返回值异常 —— 坐标转换失败 #### ❌ 问题原因: 你调用: ```js myChart.convertFromPixel({ seriesIndex: 0 }, [0, e.offsetY]) ``` 但 `convertFromPixel` 的第一个参数应该是坐标系类型,比如 `'grid'`、`'xAxis'`、`'yAxis'`,而不是 `seriesIndex`! #### ✅ 正确写法: ```js const yValue = myChart.convertFromPixel({ gridIndex: 0 }, [0, e.offsetY])[1]; ``` > 注意:必须使用 `{ gridIndex: 0 }` 来指定从哪个坐标系转换。 --- ### ✅ 3. 鼠标移动超出图表区域后停止更新(需监听全局事件) 虽然你加了 `globalout`,但某些浏览器在快速拖动时鼠标可能“跳出”canvas,导致 `mouseup` 没触发,`draggingIndex` 卡住。 建议:在 `document` 上绑定 mouseup,确保一定能释放。 --- ### ✅ 4. 图表未完全渲染就绑定事件(异步问题) 如果 DOM 没准备好或 ECharts 实例还没完成初始化,事件监听可能会失效。 ✅ 确保在 `renderChart()` 后再绑定事件,或加上防错判断。 --- ## ✅ 修复后的完整核心代码段(重点修正部分) ```js // 修正后的 mousedown myChart.getZr().on('mousedown', function (e) { if (e.target && e.target.dataIndex !== undefined && e.target.seriesIndex !== undefined) { draggingIndex = e.target.dataIndex; myChart.getZr().setCursorStyle('grabbing'); } }); // 修正后的 mousemove myChart.getZr().on('mousemove', function (e) { if (draggingIndex === null) return; const pointInPixel = [e.offsetX, e.offsetY]; if (myChart.containPixel('grid', pointInPixel)) { // ✅ 使用 gridIndex 而不是 seriesIndex const yValue = myChart.convertFromPixel({ gridIndex: 0 }, pointInPixel)[1]; baseData[draggingIndex] = Math.max(0, Math.round(yValue)); renderChart(baseData); // 重新渲染 } }); // 改进:在 document 上监听 mouseup,防止丢失事件 document.addEventListener('mouseup', () => { draggingIndex = null; myChart.getZr().setCursorStyle('default'); }); ``` --- ## ✅ 完整修复版在线测试建议 你可以将以下完整 HTML 保存为 `.html` 文件并本地打开测试: 👉 推荐使用 Chrome 浏览器,并检查控制台是否有报错。 --- ## ✅ 验证是否生效的小技巧 1. 在 `mousedown` 回调里加一个 `console.log(e.target)`; 2. 点击数据点,查看输出对象中是否有 `dataIndex` 字段; 3. 如果没有,说明你没点中 symbol —— 可尝试增大 `symbolSize` 到 `15` 方便点击。 --- ## ✅ 如何让 symbol 更容易被点击? ```js series: [{ type: 'line', symbol: 'circle', symbolSize: 15, // 更大更容易点中 emphasis: { scale: true, scaleSize: 8 // hover 时放大,提升交互体验 } }] ``` --- ## ✅ 总结:为什么原来代码“拽不动” | 原因 | 是否关键 | 修复方法 | |------|--------|---------| | 错误判断 `el.name === 'item'` | ⚠️ 高频错误 | 改为判断 `dataIndex != null` | | `convertFromPixel` 参数错误 | ⚠️ 关键错误 | 改为 `{ gridIndex: 0 }` | | 鼠标移出 canvas 导致卡住 | ⚠️ 边界问题 | 在 `document` 上监听 `mouseup` | | symbol 太小不好点 | 💡 体验问题 | 增大 `symbolSize` | --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值