2

This is a simple html for the navigation header which I use vue to show it up with v-html.

this is the demo - https://shuffle.dev/preview?project=12e60d2546392911632c3b5d1576766424f55da0&page=index.html&screen=main

<section class="text-gray-700 font-heading font-medium relative bg-gray-50 bg-opacity-50">
  <nav class="flex justify-between px-6 lg:px-12 py-8">
      .......
      .......
      <div class="mt-auto px-10">
        <button class="py-3 px-5 mt-6 w-full font-body font-bold uppercase tracking-wide text-sm border-2 border-gray-200 hover:border-gray-300 border-opacity-50 rounded-full">
          <span class="block mt-px">New project</span>
        </button>
      </div>
    </nav>
  </div>
</section>

what I want to do is to have the mouse hover on each html element such as <svg>, <button>, <div>, highlight the element and output the html element in the console.

the wishful result will be similar to the image below, a highlight on the 'New Tools' text.

enter image description here

One simple solution is to give all the elements a class and add the mouseover listener, however it raised a issue that for a complex html element with parent and child element, when the mouse is hovering over the child element, both parent and child element will be effected, how to only highlight the child element while avoid highlighting the parent element is what I am trying to figure out.

1
  • 1
    This is a duplicate question You can find more info here Commented Jan 13, 2022 at 15:03

4 Answers 4

2
+50

You can add eventListener on the buttons. With CSS you can style only the span child element. The Event Handler can console what ever you want.

<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
  <div id="app">
    <section class="text-gray-700 font-heading font-medium relative bg-gray-50 bg-opacity-50">
      <nav class="flex justify-between px-6 lg:px-12 py-8">

          <div class="mt-auto px-10">
            <button class="py-3 px-5 mt-6 w-full font-body font-bold uppercase tracking-wide text-sm border-2 border-gray-200 hover:border-gray-300 border-opacity-50 rounded-full">
              <span class="highlight block mt-px">New project</span>
            </button>
            
            <button class="py-3 px-5 mt-6 w-full font-body font-bold uppercase tracking-wide text-sm border-2 border-gray-200 hover:border-gray-300 border-opacity-50 rounded-full">
              <span class="block mt-px">New tools</span>
            </button>            
          </div>
        </nav>
      
    </section>

  </div>
</template>

<script>
export default {
  data() {
    return {      
    };
  },
  methods: {

  },
  mounted() {
    //const btns = document.querySelectorAll('button');
    const btns = document.querySelectorAll('button');
    btns.forEach(b => {
      b.addEventListener('mouseover', (e) => {
        let span = e.target.children[0];        
        console.log('child Element from hovering button', span);
      })  
    })
    
    console.log(btns)
  }
};
</script>

<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

a,
button {
  color: #4fc08d;
}
button:hover .highlight {
    background: red;
}

button {
  background: none;
  border: solid 1px;
  border-radius: 2em;
  font: inherit;
  padding: 0.75em 2em;
}
</style>
Sign up to request clarification or add additional context in comments.

1 Comment

firstly I am not only trying to highlight only the buttons, secondly I am using v-html to render the page
1

Solution:

Use the mounted hook of Vue component to fetch the dynamic HTML subtree then add 'Click' event handler for eligible node elements of the subtree to trigger element selection.

Additionally use mouseover & mouseout event Handler to toggle interactive styling

Step 1: Setup the project with a vue component using v-html

<div id="app">
  Create designs using Uinel
  <div id="interactivecanvas" v-html="dynamicHTML"></div>
  <div id="selectedElement">Selection: {{selectedElement.tagName}}</div>
</div>

Step 2: Add CSS styles to indicate a Selectable Element

.selectable{ 
  box-shadow: 0 0 0 2px green;
}

Step 3: Add a mounted hook to fetch dynamic HTML Sub-tree

 mounted(){
  
    // List of selectable Elements. Uses css selector syntax
    let selectableElements = ['li', 'span', 'button']
    selectableElements[0] = '#interactivecanvas '+selectableElements[0]

    let canvasContents = document.querySelectorAll(selectableElements.join(',
     #interactivecanvas '))
  .....

Step 4: Iterate through Sub-tree Elements and add Event Handlers for Selection

mounted(){
...
 for(let i=0; i<canvasContents.length; i++){
...
  canvasContents[i].addEventListener('click',e=>{
    e.stopPropagation();
    this.selectElement(e)
  })
...
}

Step 5: Optional! you can add mouseover & mouseout event Handler to toggle interactive styling.

Check the example below. View in full page.

new Vue({
  el: '#app',
  data() {
    return {
      selectedElement: {},
      dynamicHTML: `
        <div class="header">
          <nav class="nav">
            <ul id="nav">
              <li>Home</li>
              <li>Contact Us</li>
              <li>About Us</li>
            </ul>
            <div id="card">
              <div id="usercard">
                <span>Sona</span>
                <span>&#x25BC;</span>
              </div>
              <button>New Project</button>
            </div>
          <nav>
        </div>
      `
    }
  },
  mounted(){
  
    // List of selectable Elements. Uses css selector syntax
    let selectableElements = ['li', 'span', 'button']
    selectableElements[0] = '#interactivecanvas '+selectableElements[0]

    let canvasContents = document.querySelectorAll(selectableElements.join(', #interactivecanvas '))
    for(let i=0; i<canvasContents.length; i++){
    
      // Add mouseover & mouseout event Handler to toggle styles
      canvasContents[i].addEventListener('mouseover',(e)=>{
        e.stopPropagation();
        e.currentTarget.classList.add("selectable")
      })
      canvasContents[i].addEventListener('mouseout',(e)=>{
        e.stopPropagation();
        e.currentTarget.classList.remove("selectable")
      })
      
      // Event to fire Selection action. Use 'hover' or 'click'
      canvasContents[i].addEventListener('click',e=>{
        e.stopPropagation();
        this.selectElement(e)
      })
    }
  },
  methods:{
    selectElement(e){
      console.log('Selected '+e.currentTarget)
      this.selectedElement = e.currentTarget
    }
  }
})
* {
  box-sizing: border-box;
}

body {
  font-family: 'Verdana'
}

ul {
  list-style: none;
}

.header {
  background: rgba(245,246,247);
  padding: 20px;
}

.nav {
  display: flex;
}

.nav > ul > li {
  display: inline;
  margin: 20px;
}

.nav button {
  border: 2px solid gray;
  border-radius: 9999px;
  padding: 12px 20px;
}

.nav #card {
  display: flex;
  align-items: center;
}

#usercard {
  margin: 20px;
  padding: 10px;
}

.selectable{ 
  box-shadow: 0 0 0 2px green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  Create designs using Uinel
  <div id="selectedElement">Selection: {{selectedElement.tagName}}</div>
  <div id="interactivecanvas" v-html="dynamicHTML"></div>
</div>

Comments

0

You can use the /deep/ selector to get your styles to be applied:

<div class="description" v-if="description" v-html="description"></div>


.description {
  /deep/ p {
    margin-bottom: 10px;
  }
}

Comments

0

You could create a highlighted class like this:

.highlighted { border: 1px dashed green; }

and then add 2 functions to your component:

highlight(event) {
   console.log(event.target);
   event.target.classList.add("highlighted");
},
removeHighlight(event) {
   event.target.classList.remove("highlighted");
},

and add these attributes to the element you want to highlight and output to the console:

@mouseenter="highlight($event)" @mouseleave="removeHighlight($event)"

6 Comments

thanks, additionally another question is how would I traverse this html snippet into a array of element and add class to each element. I found var elementList = document.getElementsByTagName("*"); for(var i = 0; i < elementList.length; i++) { elementList[i].classList.add('addedclassname'); } but it seems to be working for a whole document instead of a specific html with certain id
adding @mouseenter="highlight($event)" to the html element doesn't seem work, should use addlistener?
Maybe I didn't understand at first what you were trying to do. If you're adding all of the html through v-html then it's content probably won't be parsed by Vue, so then yes, you would need to add a regular javascript event listener.
About your first question, you are using a selector that gets all elements in the page. You should use a more specific selector. Instead of getElementsByTagName("*") you could use document.querySelectorAll("#your-desired-id *")
the solution you give has a problem to me, in html there are parent element and many children elements, a lot times I just want to highlight the child element but it seems likely to also highlight its parent element which I don't it to happen.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.