纯 Javascript 或 VueJS 事件的 Mousemove 和 Hover Jquery 效果

Posted

技术标签:

【中文标题】纯 Javascript 或 VueJS 事件的 Mousemove 和 Hover Jquery 效果【英文标题】:Mousemove and Hover Jquery effect to Pure Javascript or VueJS event 【发布时间】:2021-07-17 02:59:49 【问题描述】:

我有一个 VueJS 组件,它是美国的 svg 地图图像。我之前使用 jquery 创建悬停效果来显示信息气泡。我正在尝试将其转换为纯 javascript 解决方案。鉴于 vuejs mousemove 似乎不太可靠,因此单击事件似乎是最容易实现的。我在方法中添加了点击事件,我试图捕获 SVG 路径位置并窃取顶部和左侧位置以启用那里的信息框。我试过 this.offsettop 和 this.offsetleft 但它们返回未定义。

我正在尝试转换的 Jquery 代码:

$("path, circle").hover(function(e) 
  $('#info-box').css('display','block');
  $('#info-box').html($(this).data('info'));
);

$("path, circle").mouseleave(function(e) 
  $('#info-box').css('display','none');
);

$(document).mousemove(function(e) 
  $('#info-box').css('top',e.pageY-$('#info-box').height()-30);
  $('#info-box').css('left',e.pageX-($('#info-box').width())/2);
).mouseover();

var ios = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if(ios) 
  $('a').on('click touchend', function()  
    var link = $(this).attr('href');   
    window.open(link,'_blank');
    return false;
  );

VueJS 组件

<template id="geomap-assignments">
    <v-card class="pa-4 ma-3 rounded-lg" min->
        <div class="card-title-docs">
            <h4 class="card-header">Assignments</h4>
            <h5 class="card-sub-header"></h5>
        </div>
        <div class="map-container">
            <div id="info-box"></div>
            <?xml version="1.0" encoding="utf-8" ?>
            <svg xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="us-map" preserveAspectRatio="xMinYMin meet" sodipodi:docname="Republican_Party_presidential_primaries_results,_2016.svg" inkscape:version="0.91 r13725" x="0px" y="0px"   viewBox="174 100 959 593" enable-background="new 174 100 959 593" xml:space="preserve">
            <sodipodi:namedview bordercolor="#666666" objecttolerance="10" pagecolor="#ffffff" borderopacity="1" gridtolerance="10" guidetolerance="10" inkscape:cx="509.19152" inkscape:cy="282.2353" inkscape:zoom="1.2137643" showgrid="false" id="namedview71" inkscape:current-layer="g5" inkscape:window-maximized="1" inkscape:window-y="-8" inkscape:window-x="-8" inkscape:pageopacity="0" inkscape:window- inkscape:window- inkscape:pageshadow="2">
            </sodipodi:namedview>
            <g id="g5">
                    <path id="HI" data-info="<div>State: Hawaii</div><div>Capital: Honolulu</div>" fill="#D3D3D3" d="M407.1,619.3l1.9-3.6l2.3-0.3l0.3,0.8l-2.1,3.1H407.1z M417.3,615.6l6.1,2.6l2.1-0.3l1.6-3.9   l-0.6-3.4l-4.2-0.5l-4,1.8L417.3,615.6z M448,625.6l3.7,5.5l2.4-0.3l1.1-0.5l1.5,1.3l3.7-0.2l1-1.5l-2.9-1.8l-1.9-3.7l-2.1-3.6   l-5.8,2.9L448,625.6z M468.2,634.5l1.3-1.9l4.7,1l0.6-0.5l6.1,0.6l-0.3,1.3l-2.6,1.5l-4.4-0.3L468.2,634.5z M473.5,639.7l1.9,3.9   l3.1-1.1l0.3-1.6l-1.6-2.1l-3.7-0.3V639.7z M480.5,638.5l2.3-2.9l4.7,2.4l4.4,1.1l4.4,2.7v1.9l-3.6,1.8l-4.8,1l-2.4-1.5   L480.5,638.5z M497.1,654.1l1.6-1.3l3.4,1.6l7.6,3.6l3.4,2.1l1.6,2.4l1.9,4.4l4,2.6l-0.3,1.3l-3.9,3.2l-4.2,1.5l-1.5-0.6l-3.1,1.8   l-2.4,3.2l-2.3,2.9l-1.8-0.2l-3.6-2.6l-0.3-4.5l0.6-2.4l-1.6-5.7l-2.1-1.8l-0.2-2.6l2.3-1l2.1-3.1l0.5-1l-1.6-1.8L497.1,654.1z" />
                    <path id="OK" data-info="<div>State: Oklahoma</div><div>Capital: Oklahoma City</div>" fill="#D3D3D3" d="M549.3,422.6l-10.7-0.5l-6.4-0.5l0.3,0.2l-0.7,10.4l22,1.4l32.1,1.3l-2.3,24.4l-0.5,17.8l0.2,1.6   l4.3,3.7l2.1,1.1l0.7-0.2l0.7-2.1l1.4,1.8h2.1v-1.4l2.7,1.4l-0.5,3.9l4.1,0.2l2.5,1.1l4.1,0.7l2.5,1.8l2.3-2.1l3.4,0.7l2.5,3.4h0.9   v2.3l2.3,0.7l2.3-2.3l1.8,0.7h2.5l0.9,2.5l4.8,1.8l1.4-0.7l1.8-4.1h1.1l1.1,2.1l4.1,0.7l3.7,1.4l3,0.9l1.8-0.9l0.7-2.5h4.3l2.1,0.9   l2.7-2.1h1.1l0.7,1.6h4.1l1.6-2.1l1.8,0.5l2.1,2.5l3.2,1.8l3.2,0.9l1.9,1.1l-0.4-37.2l-1.4-11l-0.2-8.9l-1.4-6.5l-0.8-7.2l-0.1-3.8   l-12.1,0.3l-46.4-0.5l-45-2.1L549.3,422.6z" />
                    <path id="KS" data-info="<div>State: Kansas</div><div>Capital: Topeka</div>" fill="#D3D3D3" d="M677.4,425.1l-12.6,0.2l-46.1-0.5l-44.6-2.1l-24.6-1.3l4.1-64.7l21.8,0.8l40.5,1.4l44.1,0.5h5.1   l3.2,3.2l2.8,0.2l0.9,1.1v2l-1.8,1.6l-0.5,2.6l2.2,3.6l2.5,3.1l2.5,2l1.1,11.2L677.4,425.1z" />
                    <path id="LA" data-info="<div>State: Louisiana</div><div>Capital: Baton Rouge</div>" fill="#D3D3D3" d="M776.2,573l-1-2.6l-1.1-3.1l-3.3-3.5l0.9-6.8l-0.1-1.1l-1.3,0.3l-8.2,0.9l-25,0.5l-0.7-2.4l0.9-8.5   l3.3-5.9l5-8.7l-0.6-2.4l1.3-0.7l0.5-2l-2.3-2.1l-0.1-1.9l-1.8-4.3l-0.5-5.9l-9.7,0.1l-19.2,0.9l-22.2,0l0,9.6l0.7,9.4l0.7,3.9   l2.5,4.1l0.9,5l4.3,5.5l0.2,3.2l0.7,0.7l-0.7,8.5l-3,5l1.6,2.1l-0.7,2.5l-0.7,7.3l-1.4,3.2l0.1,3.6l4.7-1.5l8.1-0.3l10.3,3.6   l6.5,1.1l3.7-1.5l3.2,1.1l3.2,1l0.8-2.1l-3.2-1.1l-2.6,0.5l-2.7-1.6c0,0,0.2-1.3,0.8-1.5c0.6-0.2,3.1-1,3.1-1l1.8,1.5l1.8-1   l3.2,0.6l1.5,2.4l0.3,2.3l4.5,0.3l1.8,1.8l-0.8,1.6l-1.3,0.8l1.6,1.6l8.4,3.6l3.6-1.3l1-2.4l2.6-0.6l1.8-1.5l1.3,1l0.8,2.9   l-2.3,0.8l0.6,0.6l3.4-1.3l2.3-3.4l0.8-0.5l-2.1-0.3l0.8-1.6l-0.2-1.5l2.1-0.5l1.1-1.3l0.6,0.8c0,0-0.2,3.1,0.6,3.1   c0.8,0,4.2,0.6,4.2,0.6l4,1.9l1,1.5h2.9l1.1,1l2.3-3.1v-1.5h-1.3l-3.4-2.7l-5.8-0.8l-3.2-2.3l1.1-2.7l2.3,0.3l0.2-0.6l-1.8-1v-0.5   h3.2l1.8-3.1l-1.3-1.9l-0.3-2.7l-1.5,0.2l-1.9,2.1l-0.6,2.6l-3.1-0.6l-1-1.8l1.8-1.9l2-1.8L776.2,573z" />
                    <path id="VA" data-info="<div>State: Virginia</div><div>Capital: Richmond</div>" fill="#D3D3D3" d="M1002.9,369.2l-0.1-1.9l6.5-2.5l-0.8,3.2l-2.9,3.8l-0.4,4.6l0.5,3.4l-1.8,5l-2.2,1.9l-1.5-4.6   l0.4-5.4l1.6-4.2L1002.9,369.2z M1005.2,397.5L947,410.1l-37.4,5.3l-6.7-0.4l-2.6,1.9l-7.3,0.2l-8.4,1l-8.9,1l8.5-4.9l0-2.1   l1.5-2.1l10.6-11.5l3.9,4.5l3.8,1l2.5-1.1l2.2-1.3l2.5,1.3l3.9-1.4l1.9-4.6l2.6,0.5l2.9-2.1l1.8,0.5l2.8-3.7l0.3-2.1l-1-1.3l1-1.9   l5.3-12.3l0.6-5.7l1.2-0.5l2.2,2.4l3.9-0.3l1.9-7.6l2.8-0.6l1-2.7l2.6-2.3l1.3-2.3l1.5-3.4l0.1-5.1l9.8,3.8   c0.7,0.3,0.7-4.8,0.7-4.8l4.1,1.4l-0.5,2.6l8.2,2.9l1.3,1.8l-0.9,3.7l-1.3,1.3l-0.5,1.7l0.5,2.4l2,1.3l3.9,1.4l2.9,1l4.9,0.9   l2.2,2.1l3.2,0.4l0.9,1.2l-0.4,4.7l1.4,1.1l-0.5,1.9l1.2,0.8l-0.2,1.4l-2.7-0.1l0.1,1.6l2.3,1.5l0.1,1.4l1.8,1.8l0.5,2.5l-2.6,1.4   l1.6,1.5l5.8-1.7L1005.2,397.5z" />
                    <g id="DC">
                    <path id="path58" fill="#D3D3D3" d="M975.8,353.8l-1.1-1.6l-1-0.8l1.1-1.6l2.2,1.5L975.8,353.8z" />
                    <circle id="circle60" data-info="<div>Washington DC</div>" fill="#D3D3D3" stroke="#FFFFFF" stroke- cx="975.3" cy="351.8" r="5" />
                </g>
            </g>
            <path id="path67" fill="none" stroke="#A9A9A9" stroke- d="M385,593v55l36,45 M174,525h144l67,68h86l53,54v46" />

        </svg>
        </div>
    </v-card>
</template>
<script>
    Vue.component('geomap-assignments', 
        template: '#geomap-assignments',

        methods: 
            activateCaption: function (event) 
                // `event` is the native DOM event
                if (event) 
                    const left = this.offsetTop
                    const top = this.offsetTop
                    alert("LEFT" + left + "RIGHT" + top);
                
            
        ,

        data() 
            return 
                documents: [

                ]
            
        
     )
</script>
<style scoped>
    .card-title-docs 
        margin: 20px 20px;
    

    .card-header 
        font-size: 1rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 1px;
        color: #515151;
    

    .card-sub-header 
        font-size: .8rem;
        font-weight: 500;
        text-transform: uppercase;
        letter-spacing: .7px;
        color: #7f818d;
    
    .map-container
        position:relative;
        width: 100%;
        height: 100%;
    
    #us-map 
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    

    path:hover, circle:hover 
        stroke: #002868 !important;
        stroke-width: 2px;
        stroke-linejoin: round;
        fill: #002868 !important;
        cursor: pointer;
    

    #path67 
        fill: none !important;
        stroke: #A9A9A9 !important;
        cursor: default;
    

    #info-box 
        display: none;
        position: absolute;
        top: 0px;
        left: 0px;
        z-index: 1;
        background-color: #ffffff;
        border: 2px solid #BF0A30;
        border-radius: 5px;
        padding: 5px;
        font-family: arial;
    
</style>
'''

【问题讨论】:

CSS 悬停只能影响悬停的元素,或者它之后的元素。这意味着自己、孩子、后来的兄弟姐妹和后来兄弟姐妹的孩子。在您的组件中,#info-box 位于您的 svg 之前,因此 CSS 悬停选择器无法更改其属性。要拥有纯 CSS 解决方案,您需要将 #info-box 放在您的 svg 中,这可能不是您想要的。但是,Vue 解决方案可以正常工作。 @Trevin Avery 是的,更多的挖掘让我意识到纯 css 解决方案可能不实用。我要更新我的问题。在 vueJS 方法中使用纯 JavaScript 可以吗? 那种。你应该避免使用document.getElementById() 之类的东西,因为你的组件可能有多个实例,或者其他组件可能有冲突的 ID。你应该使用$refs,如果你需要使用vanilla javascript,只需使用$el(例如this.$refs.infoBox.$el)从组件中获取元素。添加事件监听器就好了。确保将它们添加到 mounted() 并在 beforeDestroy()destroyed() 中删除它们。 @Trevin Avery 我已经调整了我的代码以改为使用悬停,因为看起来 VueJS mousemove 与仅执行单击事件有点不一致。我已经更新了我上面的问题。我需要获取路径的顶部和左侧位置以显示我的信息框。我尝试了 this.offsetTop 和 this.offsetLeft ,它返回未定义的值。这是因为它是路径元素吗? 我不确定为什么偏移不起作用。但是getBoundingClientRect() 确实有效。您可以在路径上执行此操作以获取视口中路径的左上角。然后,由于您的#info-box.map-container 内,您需要调整该容器的位置,或者将#info-box 更改为固定位置。要调整位置,只需在.map-container#us-map 上使用getBoundingClientRect() 函数,然后减去顶部和左侧。 【参考方案1】:

在问题的cmets中找到了解决方案。

总之,问题在于理解如何定位#info-box 元素。使用getBoundingClientRect() 函数,我们可以获得任何元素相对于整个文档的位置。要获得点击路径元素的位置,只需从路径的顶部和左侧减去#us-map 的顶部和左侧。这将给出路径左上角相对于包含元素的位置。然后我们可以使用它来定位#info-box

【讨论】:

以上是关于纯 Javascript 或 VueJS 事件的 Mousemove 和 Hover Jquery 效果的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs v-for 根据某些条件添加标签属性或事件监听器

VueJS 事件修饰符

VueJS 事件修饰符

No.5一步步学习vuejs之事件监听和组件

[javascript] vuejs的elementui配合iframe实现页面跳转

[javascript] vuejs的elementui配合iframe实现页面跳转