简体   繁体   中英

Unable to access object property of state object in state modification method (TypeError)

I'm working on a text analyzer in jQuery that returns word count, unique word count, average word length, and average sentence length.

I had it working (at least halfway, up to the unique word count functionality) before I realized my structure was horrible. So I refactored it...and now I'm having trouble getting it to work at all.

On line 65, I'm getting TypeError: Cannot read property 'length' of null . This is in reference to state.sentences , which when I console.log, I get null . I just noticed that when I type in a full sentence as my input, that doesn't come up (and it logs the sentence correctly), but it's still not rendering the content to the DOM.

What am I doing wrong here? Something about the way I'm trying to access the state object, obviously -- but what, exactly, is beyond me.

Here is the index.js:

'use strict'

// state object

var state = {
    text: "",
    words: [],
    uniqueWords: [],
    sentences: [],
    wordLengths: [],
    sentenceLengths: [],
    wordCount: 0,
    uniqueWordCount: 0,
    averageWordLength: 0,
    averageSentenceLength: 0
}


//state modification functions

var getText = function(state) {
    state.text = $('#user-text').val()
}

var getWords = function(state) {
    state.words = state.text.match(/[^_\W]+/g)
    //need to also change all uppercase to lowercase
}

var getSentences = function(state) {
    state.sentences = state.text.match( /[^\.!\?]+[\.!\?]+/g )
}

var getUniqueWords = function(state) {
    for (var i = 0; i < state.words.length; i++) {
        if (state.uniqueWords.indexOf(state.words[i]) < 0) {
            state.uniqueWords.push(state.words[i])
        }
    }
}

var getWordCount = function(state) {
    state.wordCount = state.words.length
}

var getUniqueWordCount = function(state) {
    state.uniqueWordCount = state.uniqueWords.length
}

var getWordLengths = function(state) {
    for (var i = 0; i < state.words.length; i++) {
       state.wordLengths.push(state.words[i].length)
       console.log(state.wordLengths)
    }
}

var getAverageWordLength = function(state) { 
    var sum = state.wordLengths.reduce(function(a, b) {
        return a + b
    }, 0)
    state.averageWordLength = sum/state.wordLengths.length
}

var getSentenceLengths = function(state) {
    for (var i = 0; i < state.sentences.length; i++) {
        state.sentenceLengths.push(state.sentences[i].length)
    }
}

var getAverageSentenceLength = function(state) {
    var sum = state.sentenceLengths.reduce(function(a,b) {
       return a + b
    }, 0)
   state.averageSentenceLength = sum/state.sentenceLengths.length
}

// render functions

var renderWordCount = function(state, element) {
    $("dl").toggleClass('hidden')
    return element.append(state.wordCount)
}

var renderUniqueWordCount = function(state, element) {
    $("dl").toggleClass('hidden')
    return element.append(state.uniqueWordCount)
}

var renderAverageWordLength = function(state, element) {
    $("dl").toggleClass('hidden')
    return element.append(state.averageWordLength)
}

var renderAverageSentenceLength = function(state, element) {
    $("dl").toggleClass('hidden')
    return element.append(state.averageSentenceLength)
}

// event listener functions

$(function() {
    $('button').click(function() {
        event.preventDefault()
        getText(state)
        getWords(state)
        getSentences(state)
        getUniqueWords(state)
        getWordCount(state)
        getUniqueWordCount(state)
        getAverageWordLength(state)
        getSentenceLengths(state)
        getAverageSentenceLength(state)
        renderWordCount(state, $('.wordCount'))
        renderUniqueWordCount(state, $('.uniqueWordCount'))
        renderAverageWordLength(state, $('.averageWordLength'))
        renderAverageSentenceLength(state, $('.averageSentenceLength'))
    })
})

And here is the index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Text analyzer</title>
    <meta charset="utf-8">

    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css">
    <link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
    <div class="container">
        <main>
            <h1>Text analzyer</h1>
            <p>Paste in text below, submit, and get some basic stats back.</p>
            <form class="js-form">
                <div>
                    <label for="user-text">Text to analyze</label>
                    <textarea cols="60" rows="20" id="user-text" name="user-text" placeholder="What have you got to say?" required></textarea>
                </div>
                <div>
                    <button type="submit">Analyze it!</button>
                </div>
            </form>

            <dl class="hidden text-report">
                <dt>Word count</dt>
                <dd class="wordCount"></dd>

                <dt>Unique word count</dt>
                <dd class="uniqueWordCount"></dd>

                <dt>Average word length</dt>
                <dd class="averageWordLength"></dd>

                <dt>Average sentence length</dt>
                <dd class="averageSentenceLength"></dd>
            </dl>
        </main>
    </div>
    <script src="jquery-3.1.1.js"></script>
<!-- <script src="app.js"></script> -->
    <script src="index.js"></script>
</body>
</html>

Thank you!

PS If you have any ideas about structuring the app, all thoughts are welcome; I especially am concerned about the way I'm calling all the functions one after another in the ready function at the end. That seems kinda messy for some reason.

Your problem lies in here:

$(function() {
    $('button').click(function() {
        event.preventDefault()
        getText(state) // <---at this point you are passing an object to set the text in.
        // state object but getText has some-thing which is not correct
        //.....other too
    })
})

var getText = function(state) {
    state.text = $('user-text').val() // <-----Here `user-text` is not a valid
    // html element and jquery doesn't recognize it.
    // So, You should change it to a valid css selector.
}

So, eventually you should use a Id selector:

$('#user-text').val() 

As your html element has Id attribute:

<textarea cols="60" rows="20" 
          id="user-text" 
          name="user-text" 
          placeholder="What have you got to say?" required></textarea>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM