为什么这个JavaScript函数在首次加载时没有正确计算,但之后工作正常?

I am working on a site that generates notation for a random musical rhythm based on some user-selected parameters. It does this by using an ajax call, which returns a random set of <img> elements that represent different notes. I have a function that is designed to scale the rhythm to fit in the screen, regardless of it's actual size.

The function is triggered after a successful ajax call, which is triggered by a click event on a button.

My problem is that the function does not work as desired when running the first time the page is loaded.

After the function runs for the first time, the height attribute of all of the <img> elements is somehow set to 0.

However, the function works great if I run the it again (by clicking the button). It also works fine after a page refresh.

Also, I do not have this issue in IE11, only Chrome (I haven't tested other browsers yet).

I have tried wrapping the code in both $(window).load() and $(document).ready() event handlers, but this didn't help.

The site in action can be found at http://www.rhythmrandomizer.com

Any help would be greatly appreciated!

Below is the relevant code:

Event handler for the button:

$("#randomize").click(function(){
    //get general options from form
    var timeSignature = $("#timeSignature").val();
    var phraseLength = $("#phraseLength").val();

    //get note options from form
    var checked = [];
    $("#noteOptions :checked").each(function() {
        checked.push($(this).val());
    });

    //alert user and exit function if nothing is selected
    if (checked.length < 1) {
        alert("Please select at least one note value");
        return;
    }


    //format note option ids into a delimited string
    var noteOptions = "";
    for (var i=0; i < checked.length; i++) {
        noteOptions += checked[i] + "a";
    }

    //remove the final comma and space
    noteOptions = noteOptions.substr(0, noteOptions.length - 1);

    //ajax call
    $.ajax("randomize.php", {
        data : {
            timeSignature : timeSignature,
            phraseLength : phraseLength,
            noteOptions : noteOptions
        },
        type : "GET",
        success : function(response) {
            $("#rhythm").html(response);
            scaleRhythm();
        },
        error : function(xhr, status, errorThrown) {
            console.log(status + " | " + errorThrown);
        }
    }); 
});

The php file that returns the rhythm notation:

<?php
//MySQL connection variables
$hostname = 'localhost';
$user = ini_get('mysqli.default_user');
$pw = ini_get('mysqli.default_pw');
$database = 'rhytxfpd_rhythmrandomizer';

//Connect to database
try {
    $db = new PDO('mysql:host=' . $hostname . ';dbname=' . $database,$user,$pw);
} catch(PDOException $e) {
    echo $e->getMessage();
    die();
}

//Get values from GET
$timeSignature = $_GET['timeSignature'];
$phraseLength = $_GET['phraseLength'];
$noteOptString = $_GET['noteOptions'];

//Split up note options string
$noteOptions = explode('a', $noteOptString);

//Create sql query
$sql = 'SELECT
            noteName,
            noteValue,
            noteGraphic
        FROM
            notes
        WHERE';

//append noteOptions as WHERE clauses
foreach ($noteOptions as $opt) {
    $sql = $sql . ' noteGroupID = ' . $opt . ' OR';
}

//remove final " OR"
$sql = substr($sql, 0, strlen($sql) - 3);

//query the database and get all results as an array
/* This will return a table with the name, graphic, and value of 
 * the notes that the user selected prior to submitting the form
 */
$stmt = $db->query($sql);
$result = $stmt->fetchAll();

//Get the total number of options selected
$numOpts = count($result);

/***************************/
/** BEGIN PRINTING RHYTHM **/
/***************************/

//div to begin the first measure
echo '<div class="measure" id="m1' . $measure . '">';

//Print time signature
echo '<img class="note" src="notes/' . $timeSignature . '.png" title="time signature ' . 
        $timeSignature . '/4" alt="time signature ' . $timeSignature . '/4"/>';

//Prints as many measures as indicated by the phrase length selection
$measure = 1;
while ($measure <= $phraseLength) {

    //begin a new div for other measures.
    if ($measure != 1) {
        echo '<div class="measure" id="m' . $measure . '">';
    }

    //Prints random measure according to time signature
    $beats = 0;
    while ($beats < $timeSignature) {
        //Generate a random number
        $random = rand(0, $numOpts - 1);

        //Get the random note from results
        $note = $result[$random];

        //Continues if chosen note will not fit in the measure
        if ($beats + $note['noteValue'] > $timeSignature) {
            continue;
        }

        //Prints random note
        echo '<img class="note" src="notes/' . $note['noteGraphic'] . '.png" title="' .
                $note['noteName'] . '" alt="' . $note['noteName'] . '"/>';

        //Adds random note's value to total number of beats
        $beats += $note['noteValue'];
        //$beats++;
    }

    //If last measure
    if ($measure == $phraseLength) {
        echo '<img class="note" src="notes/1.png" title="double barline" alt="double barline"/>';
        echo '</div>';
    } else {
        echo '<img class="note" src=notes/b.png title="barline" alt="barline"/>';
        echo '</div>';
    }

    //Increment to next measure
    $measure++;
}

The scaleRhythm() function:

function scaleRhythm() {
    //Get width of rhythm at full resolution
    var rhythmWidth = $("#rhythm").width();

    //Get current screen/window width
    var screenWidth = window.innerWidth;

    //Compute ratio between curren screen and window widths
    var ratio =   screenWidth / rhythmWidth;

    //Multiply img note height by ratio, then by 90% to provide some
    //breathing room on either side of the rhythm
    var newHeight = (400 * ratio) * .9;

    //Set img note height to new height or 300px, whichever is smaller
    if (newHeight < 300) {
        $(".note").css("height",newHeight);
        //code to center rhythm horizontally
        $("#rhythm").css("margin-top",(300-newHeight)/2);
    } else {
        $(".note").css("height",300);
        $("#rhythm").css("margin-top",0);
    }
}

Add this javascript to your <script></script>:

$(function(){ $("#randomize").click(); });

This will cause your page to run the function that populates your random elements then (at the end of that function) run the scale function.

I tested it by running it on your page in the chrome console and it worked.

If you put a breakpoint in the scaleRhythm function you'll notice on page load it's not being run. You've defined the function but it is not being called on page load. In fact none of the code you want run (ie: the ajax call) gets called until the first click happens. So what you need to do is trigger the click event on the button like JRulle said.

$("#randomize").click();

Okay so here is your issue. The first time you click the button, var rhythmWidth = $("#rhythm").width(); evaluates to "0" because it is empty.

Which causes these subsequent functions to be "0" as well:

var ratio =   screenWidth / rhythmWidth;
var newHeight = (400 * ratio) * .9;

I would edit your function to be like so:

var rhythmWidth = $("#rhythm").width();
if (rhythmWidth == 0) { rhythmWidth = 10; } //assign some reasonable value here

since your function does not support a rhythmWidth of "0"