1

I'm writing a page with vue.js and axios to grab the content of the newest post from a category of a WP site by means of REST API. The post content is always an ordered list (OL). The items of the injected OL will then be displayed as a carousel. The OL is injected successfully, but fail to be manipulated. Is there anyone would kindly point out the problem in the JS I wrote? The same task can be done easily using jQuery, but I just want to practice something new.

HTML:

<div class="frame ajax annoucements">
  <h1 id="announcements-title" class="material ajax-title">Announcements<span class="title-this-week"></span></h1>
  <div id="app" class="ajax-body material" v-html="posts[0].content.rendered">
  </div>
</div>

JS:

var tcnt = 0;

new Vue({
  el: '#app',
  data: {
    posts: []
  },
  created() {
    axios.get('http://www.just-a-wp-site.com/wp-json/wp/v2/categories/')
    .then((response) => {
      var categoryId = 0;
      response.data.forEach(function(category){
        if (category.slug == 'announcements') {
          categoryId = category.id;
          console.log('Category ID: ' + category.id);
        }
      });
      return categoryId;
    })
    .then((categoryId) => {
      console.log(categoryId);
      return axios.get('http://www.just-a-wp-site.com/wp-json/wp/v2/posts/', {
        params: {
          categories: categoryId,
          per_page: 1,
          status: 'publish'
        }
      });
    })
    .then((response) => {
      console.log(response.data);
      this.posts = response.data;
    })
    .catch((error) => {
      console.log(error.message);
    })
  },
  mounted() {
    var announcements = document.querySelectorAll('frame.ajax .ajax-body > ol > li');
    console.log(announcements.length);
    setInterval(function(){
      var target = announcements.length % tcnt;
      for (i = 0; i < announcements.length; i++) {
        if (i == target) {
          announcements[i].style.display = 'block';
        }
        else {
          announcements[i].style.display = 'initial';
        }
      }
      tcnt++;
    }, 1000);
  }
});
1
  • Working with the DOM directly in a vue project is not the best practice, and I'll give you an example later to implement the same functionality. And conent which rendered by v-html is Non-responsive. Commented May 21, 2018 at 9:00

2 Answers 2

2

It's a timing issue.

You will need to understand the asynchronous nature of Javascript./

When you do document.querySelectorAll('frame.ajax .ajax-body > ol > li') in mounted hook, the ajax request probably hasn't responded yet. Therefore, the DOM elements do not exist yet and it does not work as expected.

My suggestion is to put the codes for DOM manipulation into a method and call it after the ajax request has responded.

See listManipulation method:

var tcnt = 0;

new Vue({
    el: '#app',
    data: {
        posts: []
    },
    created() {
        axios.get('http://www.just-a-wp-site.com/wp-json/wp/v2/categories/')
            .then((response) => {
                var categoryId = 0;
                response.data.forEach(function(category) {
                    if (category.slug == 'announcements') {
                        categoryId = category.id;
                        console.log('Category ID: ' + category.id);
                    }
                });
                return categoryId;
            })
            .then((categoryId) => {
                console.log(categoryId);
                return axios.get('http://www.just-a-wp-site.com/wp-json/wp/v2/posts/', {
                    params: {
                        categories: categoryId,
                        per_page: 1,
                        status: 'publish'
                    }
                });
            })
            .then((response) => {
                console.log(response.data);
                this.posts = response.data;
                this.$nextTick(() => {
                    this.listManipulation();
                })
            })
            .catch((error) => {
                console.log(error.message);
            })
    },
    methods: {
        listManipulation() {
            var announcements = document.querySelectorAll('frame.ajax .ajax-body > ol > li');
            console.log(announcements.length);
            setInterval(function() {
                var target = announcements.length % tcnt;
                for (i = 0; i < announcements.length; i++) {
                    if (i == target) {
                        announcements[i].style.display = 'block';
                    } else {
                        announcements[i].style.display = 'initial';
                    }
                }
                tcnt++;
            }, 1000);
        }
    }
});

$nextTick means wait for the DOM to update first, then executes the code inside.


It's also considered bad practice to manipulate DOM directly in a Vue project. Because Vue keeps track of the DOM changes using Virtual DOM. If you modify the DOM, Vue won't keep track of that and may overwrite the modification the next time it updates.

Since you are controlling the display, you should check out v-if

Sign up to request clarification or add additional context in comments.

Comments

1

give you an example for carousel,without jQuery and any DOM operations:

var app = new Vue({
	el: '#app',
	data() {
		return {
			list: [1,2,3,4],
			curIndex: 0
		}
	},
	created () {
		window.setInterval(_=>{
			if(this.curIndex+1>this.list.length-1){
				this.curIndex = 0;
			}else{
				this.curIndex++
			}
		},3000)
	}
})
.wrapper{
		width: 200px;
		height: 100px;
		position: relative;
	}
	.wrapper>div{
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100px;
		line-height: 100px;	
		text-align: center;
		color: #fff;	
	}
	.wrapper>div:nth-child(1){
		background: red;
	}
	.wrapper>div:nth-child(2){
		background: blue;
	}
	.wrapper>div:nth-child(3){
		background: orange;
	}
	.wrapper>div:nth-child(4){
		background: yellow;
	}
	.fade-enter,.fade-leave-active{
		opacity: 0;
	}
	.fade-enter-active,.fade-leave-active{
		transition: opacity .5s ease-in-out;
	}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  	<transition-group tag="div" class="wrapper" name="fade">
  		<div v-for="(item,index) of list" :key="index" v-show="index==curIndex">
			{{item}}
		</div>
	</transition-group>
</div>

2 Comments

What you need to focus on is just the contents of the list, not how to manipulate the DOM elements
Thank you very much. Yes, manipulating DOM elements directly still sounds quite "jQuery", but it is needed in this case because I need to grab the post content in HTML directly. However, your answer helped me a lot in another page, which I grab a JSON object instead.

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.