kirqe
kirqe

Reputation: 2470

Why am I getting space stacking v-for list and a regular a tag?

I'm rendering a list of icon-links, using v-for and right after it adding a few other icon-list items.

Why am I getting space with the items attached at the end? Obviously, I checked my css and it has no rules for any margin, padding, etc.

As you can see, without v-for, every item in the list have this space around it.

<span style="padding-top:10px;display: block;">
  <a v-for="s in social.sites" :href="s.userPageUrl" target="_blank">
    <img :src="s.iconUrl" style="display: inline-block; margin-bottom: 4px;" nosend="1" :width="social.wh" :height="social.wh">
  </a>
  <a :href="contacts.skype"><img src="imgurl" style="display: inline-block; margin-bottom: 4px;" nosend="1" :width="social.wh" :height="social.wh"></a>
  <a :href="contacts.skype"><img src="imgurl" style="display: inline-block; margin-bottom: 4px;" nosend="1" :width="social.wh" :height="social.wh"></a>
  <a :href="contacts.skype"><img src="imgurl" style="display: inline-block; margin-bottom: 4px;" nosend="1" :width="social.wh" :height="social.wh"></a>
</span>

new Vue({
  el: "#app",
  data: {
    demoL: "https://i.imgur.com/Nk3PKVZ.png",
    social: {
      sites: [
       { 
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png",
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       }
    ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<span style="display: block;">
  <a v-for="s in social.sites" :href="s.userPageUrl">
    <img :src="s.iconUrl" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20">
  </a>
  
  
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
</span>

</div>

Upvotes: 1

Views: 119

Answers (1)

tao
tao

Reputation: 90013

The classic (block level) solution is to give the children:

a {
  display: inline-block;
  float: left;
}

and apply overflow: hidden; to parent (extends its background to include all floating children).

The modern solution, applicable to your case, is to give the parent display:flex (if there's any case in which you want the children to wrap, also add flex-wrap:wrap to it).


Why?
By default, anchors have display:inline. Which means they are displayed as letters in text, as far as you're concerned. When you write l e t t e r s you don't expect browsers to remove the spaces between your letters.
Why do you expect it when the letters are... anchors?

As an alternative, you can simply remove any spaces or tabs between the end of an <a> tag and the beginning of the next.

The above is a bit of a simplification, but it's enough to understand the cause.

Here's why it's a simplification: any display:inline element can contain other elements and spaces, which will determine how the inline content will wrap, according to text-wrap property. Or, its elements can have a display value other than inline which will disrupt the inline behaviour of the parent as well. So a better association would be with words in text, not with letters.

For the intents and purposes of your question, either making the children inline-blocks and floating them or giving the parent a display value of flex or inline-flex will work. Alternatively, you could effectively remove any space, tab or new line character in between your > and <.

A handy way of removing the spaces (while keeping your code in a readable position) is to place the ending character of your closing tag on the next line:

new Vue({
  el: "#app",
  data: {
    demoL: "https://i.imgur.com/Nk3PKVZ.png",
    social: {
      sites: [
       { 
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png",
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       }
    ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<span style="display: block;">
  <a v-for="s in social.sites" :href="s.userPageUrl">
    <img :src="s.iconUrl" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20">
  </a
  ><a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a
  ><a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a
  ><a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a></span></div>

However, this technique is not recommended in production environments, especially when more than one developer is working on the same codebase, as it's too fragile and errors would be hard to spot.

Controlling their display behavior via CSS is the proper way to go.

Flexbox solution:

new Vue({
  el: "#app",
  data: {
    demoL: "https://i.imgur.com/Nk3PKVZ.png",
    social: {
      sites: [
       { 
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png",
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       }
    ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<span style="display:flex;">
  <a v-for="s in social.sites" :href="s.userPageUrl">
    <img :src="s.iconUrl" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20">
  </a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a></span></div>

Inline blocks solution:

new Vue({
  el: "#app",
  data: {
    demoL: "https://i.imgur.com/Nk3PKVZ.png",
    social: {
      sites: [
       { 
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png",
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       },
       {
         iconUrl: "https://i.imgur.com/Nk3PKVZ.png", 
         userPageUrl: "sdf"
       }
    ]
    }
  }
})
#app > span {
  overflow: hidden;
}
#app > span > a {
  display: inline-block;
  float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<span style="display:block;">
  <a v-for="s in social.sites" :href="s.userPageUrl">
    <img :src="s.iconUrl" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20">
  </a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a>
  <a href=""><img :src="demoL" style="display: inline-block; margin-bottom: 4px;" nosend="1" width="20" height="20"></a></span></div>

Upvotes: 3

Related Questions