为什么调用jquery-jcrop的destroy()方法会销毁我的Leaflet地图?
问题描述
我使用jcrop做了一个简单的可运行示例,可以在图片上生成选择框:
$('#container').Jcrop({ onSelect: function(c){ alert(JSON.stringify(c)); this.destroy() } })
<meta charset="UTF-8"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.15/css/jquery.Jcrop.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.15/js/jquery.Jcrop.js"></script> <img src="https://d3o1694hluedf9.cloudfront.net/market-750.jpg" id="container">
这个示例里this.destroy()运行完全符合预期,只会销毁jcrop生成的阴影层,到这一步所有功能都正常。但我把同样的逻辑用到Leaflet地图上的时候就出问题了,以下是我的实现代码:
var mymap = L.map('container', { center:[-23.553670644165493, -46.648217439651496], zoom:18, maxZoom:19, zoomControl:false, attributionControl:false }); var lyrOSRHOT = L.tileLayer.provider('OpenStreetMap.HOT'); mymap.addLayer(lyrOSRHOT); $('#container').Jcrop({ onSelect: function(c){ alert(JSON.stringify(c)); this.destroy() } })
#container{ height: 100%; position: absolute; z-index: 0; }
<meta charset="UTF-8"> <!-- Leaflet --> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet-src.min.js" integrity="sha512-XQr+/1RXvYozXiwrumwtu3lqQmVwZ8nkLUrC/mc3HBHw4Imh++RXjwtLQFuOz3i65j9CSfKt50x6w/uUY2ovOQ==" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-providers/1.12.0/leaflet-providers.min.js" integrity="sha512-LixflAm9c0/qONbz9F1Ept+QJ6QBpb7wUlPuyv1EHloTVgwSK8j3yMV3elnElGQcv7Y5QTFlF/FqyeE/N4LnKQ==" crossorigin="anonymous"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="anonymous" /> <!-- jQUERY JCROP--> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.15/css/jquery.Jcrop.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.15/js/jquery.Jcrop.js"></script> <div id="container" style="height:100vh; width: 100vw"></div>
我完全照搬了第一个示例的逻辑,但选中地图区域后调用destroy()会把Leaflet地图一起销毁,绑定图片的时候就不会有这个问题。请问是什么原因导致的?是jcrop和Leaflet有冲突吗,必须修改库源码才能实现需求吗?
问题原因
这个问题和库冲突无关,也不需要改源码,核心是你的DOM绑定逻辑错了:
你用的0.9.15版本Jcrop是专门为图片裁剪设计的,它的初始化和销毁逻辑有明确的适用场景:
- 绑定到
<img>元素时,Jcrop只会在图片外层追加自己生成的阴影、选框等节点,destroy()执行时只会移除自身追加的内容,完全不会动原来的img元素,所以第一个示例正常。 - 绑定到已经有子元素的
<div>(也就是你已经渲染完Leaflet地图的容器)时,Jcrop初始化时会把div内所有已有的子元素(Leaflet生成的所有地图DOM节点)都纳入自己的管理范围,执行destroy()时会把这些子元素也一并清除,表现为地图被一起销毁。
解决方法
只需要调整DOM结构,不要把Jcrop直接绑定到Leaflet的容器上即可,步骤如下:
- 保留原有的Leaflet容器
#container,给它加position: relative属性 - 在
#container内部插入一个和它宽高100%一致的绝对定位透明div,比如id设为jcrop-wrapper,z-index设置为高于Leaflet的地图层即可 - 把Jcrop绑定到这个新增的
jcrop-wrapper上即可,destroy()执行时只会清理这个div上的Jcrop元素,完全不会影响Leaflet的地图内容。
修改后的核心代码示例:
<div id="container" style="height:100vh; width: 100vw; position: relative;"> <div id="jcrop-wrapper" style="position: absolute; top:0; left:0; width:100%; height:100%; z-index: 1000;"></div> </div>
// 原Leaflet初始化逻辑不变 var mymap = L.map('container', { center:[-23.553670644165493, -46.648217439651496], zoom:18, maxZoom:19, zoomControl:false, attributionControl:false }); var lyrOSRHOT = L.tileLayer.provider('OpenStreetMap.HOT'); mymap.addLayer(lyrOSRHOT); // Jcrop绑定到新增的透明层上 $('#jcrop-wrapper').Jcrop({ onSelect: function(c){ alert(JSON.stringify(c)); this.destroy() } })
内容的提问来源于stack exchange,提问作者raylight
相关产品推荐
相关产品推荐

