委任されたイベントハンドラーの場合、次のようなものがあります。
<ul>
    <li data-id="1">
        <span>Item 1</span>
    </li>
    <li data-id="2">
        <span>Item 2</span>
    </li>
    <li data-id="3">
        <span>Item 3</span>
    </li>
    <li data-id="4">
        <span>Item 4</span>
    </li>
    <li data-id="5">
        <span>Item 5</span>
    </li>
</ul>
そしてあなたのJSコードはそうです:
$(document).ready(function() {
    $('ul').on('click li', function(event) {
        var $target = $(event.target),
            itemId = $target.data('id');
        //do something with itemId
    });
});
undefinedLIのコンテンツはにラップされているため<span>、<span>おそらくitemIdがイベントターゲットになるため、itemIdがであることがわかります。次のような簡単なチェックでこれを回避できます。
$(document).ready(function() {
    $('ul').on('click li', function(event) {
        var $target = $(event.target).is('li') ? $(event.target) : $(event.target).closest('li'),
            itemId = $target.data('id');
        //do something with itemId
    });
});
または、読みやすさを最大化したい場合(およびjQueryラッピング呼び出しの不要な繰り返しを回避する場合):
$(document).ready(function() {
    $('ul').on('click li', function(event) {
        var $target = $(event.target),
            itemId;
        $target = $target.is('li') ? $target : $target.closest('li');
        itemId = $target.data('id');
        //do something with itemId
    });
});
イベントの委任を使用する場合、この.is()メソッドは、(とりわけ)イベントターゲットが実際に必要なものであることを確認するために非常に役立ちます。を使用.closest(selector)してDOMツリーを上方向に検索し、を使用して.find(selector)(一般にと.first()同様に.find(selector).first())、下方向に検索します。を使用する.first()場合.closest()は、最初に一致する祖先要素のみを返し、一致する.find()すべての子孫を返すため、を使用する必要はありません。