$(this) 未在 jQuery UI 对话框的“打开”选项中设置

$(this) not set in the jQuery UI dialog "open" option

提问人:Alexander Farber 提问时间:10/14/2023 最后编辑:Alexander Farber 更新时间:10/15/2023 访问量:49

问:

在文字游戏的底部有 4 个按钮,用于打开带有游戏字典中某些单词的 jQuery UI 对话框:

game screenshot

我正在尝试通过创建以下函数来简化游戏代码:

// select word using the filterFunc and then concat them all to a string
function filterWords(filterFunc) {
    // the words already filtered and assigned to the dialog's innerHTML
    if ($(this).html().length > 1000) {
        return (ev, ui) => {};
    }

    // filter the keys of HASHED dictionary by calling the filterFunc on each
    const filtered = Object.keys(HASHED)
        .filter(word => filterFunc(word))
        .reduce((result, word) => {
            return result + 
                '<p><span class="tile">' + word + '</span> ' + HASHED[word] + '</p>'
        }, '');

    // return the closure expected by the dialog's "open" option
    return (ev, ui) => {
        $(this).html(filtered);
        const title = $(this).dialog('option', 'title');
        console.log(title + ': ' + filtered.length + ' chars');
    };
}

我希望jQuery UI对话框“打开”选项需要一个,这就是我试图通过我的新函数给它的东西:function (ev, ui) {}

const twoDlg = $('#twoDlg').dialog({
    modal: true,
    appendTo: '#fullDiv',
    autoOpen: false,
    open: filterWords(word => word.length == 2),
    buttons: {
        'Close': function() {
            $(this).dialog('close');
        }
    }
});

这是另一个对话框:

const rare2Dlg = $('#rare2Dlg').dialog({
    modal: true,
    appendTo: '#fullDiv',
    autoOpen: false,
    open: filterWords(word => word.indexOf('X') >= 0),
    buttons: {
        'Close': function() {
            $(this).dialog('close');
        }
    }
});

不幸的是,现在我收到错误消息:

jquery.js:4095  Uncaught TypeError: Cannot read properties of undefined (reading 'length')
    at filterWords (test?player=abcde:833:594)
    at HTMLDocument.<anonymous> (test?player=abcde:835:139)
    at mightThrow (jquery.js:3802:29)
    at process (jquery.js:3870:12)

这向我表明这在我的关闭中是无效的。$(this).html()

有没有办法让它工作?

更新:

我准备了一个带有 3 个对话框和 3 个按钮的 jsFiddle 可以打开。该方法有效,但重复代码过多。注释掉的方法失败。filterWordsWorks()open: filterWordsBroken(word => word.length == 2),

在相同的演示代码下面,内联到 Stackoverflow 中:

'use strict';

const HASHED = {
  "one": "Word description 1",
  "two": "Word description 2",
  "three": "Word description 3",
  "four": "Word description 4",
  "five": "Word description 5",
  "six": "Word description 6",
  "seven": "Word description 7"
};

// select word using the filterFunc and then concat them all to a string
function filterWordsBroken(filterFunc) {
  // the words already filtered and assigned to the dialog's innerHTML
  if ($(this).html().length > 1000) {
    return (ev, ui) => {};
  }

  // filter the keys of HASHED dictionary by calling the filterFunc on each
  const filtered = Object.keys(HASHED)
    .filter(word => filterFunc(word))
    .reduce((result, word) => {
      return result +
        '<p>' + word + ': ' + HASHED[word] + '</p>'
    }, '');

  // return the closure expected by the dialog's "open" option
  return (ev, ui) => {
    $(this).html(filtered);
    const title = $(this).dialog('option', 'title');
    console.log(title + ': ' + filtered.length + ' chars');
  };
}

// select word using the filterFunc and then concat them all to a string
function filterWordsWorks(filterFunc) {
  return Object.keys(HASHED)
    .filter(word => filterFunc(word))
    .reduce((result, word) => {
      return result +
        '<p>' + word + ': ' + HASHED[word] + '</p>'
    }, '');
}

jQuery(document).ready(function($) {
  const twoDlg = $('#twoDlg').dialog({
    modal: true,
    autoOpen: false,
    //open: filterWordsBroken(word => word.length == 2),
    open: function(ev, ui) {
      // prevent this code from running twice
      if ($(this).html().length < 1000) {
        const filtered = filterWordsWorks(word => word.length == 2);
        $(this).html(filtered);
        const title = $(this).dialog('option', 'title');
        console.log(title + ': ' + filtered.length);
      }
    }
  });
  const threeDlg = $('#threeDlg').dialog({
    modal: true,
    autoOpen: false,
    //open: filterWordsBroken(word => word.length == 3),
    open: function(ev, ui) {
      // prevent this code from running twice
      if ($(this).html().length < 1000) {
        const filtered = filterWordsWorks(word => word.length == 3);
        $(this).html(filtered);
        const title = $(this).dialog('option', 'title');
        console.log(title + ': ' + filtered.length);
      }
    }
  });
  const fourDlg = $('#fourDlg').dialog({
    modal: true,
    autoOpen: false,
    //open: filterWordsBroken(word => word.length == 3),
    open: function(ev, ui) {
      // prevent this code from running twice
      if ($(this).html().length < 1000) {
        const filtered = filterWordsWorks(word => word.length == 4);
        $(this).html(filtered);
        const title = $(this).dialog('option', 'title');
        console.log(title + ': ' + filtered.length);
      }
    }
  });

  $('#twoBtn').button().click(function(ev) {
    ev.preventDefault();
    twoDlg.dialog('open');
  });

  $('#threeBtn').button().click(function(ev) {
    ev.preventDefault();
    threeDlg.dialog('open');
  });

  $('#fourBtn').button().click(function(ev) {
    ev.preventDefault();
    fourDlg.dialog('open');
  });
});
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-ui@1/dist/themes/redmond/jquery-ui.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ui@1/dist/jquery-ui.min.js"></script>

<!-- beware: the twoDlg will be empty -->
<DIV ID="twoDlg" TITLE="2 letters"></DIV>
<DIV ID="threeDlg" TITLE="3 letters"></DIV>
<DIV ID="fourDlg" TITLE="4 letters"></DIV>

<BUTTON ID="twoBtn">2 letters</BUTTON>
<BUTTON ID="threeBtn">3 letters</BUTTON>
<BUTTON ID="fourBtn">4 letters</BUTTON>

javascript 闭包 jquery-ui-dialog

评论

0赞 freedomn-m 10/14/2023
open: filterWords(word => word.indexOf('X') >= 0),将结果返回到选项 - 您可能想要(不要太仔细查看)filterWords(word => word.indexOf('X') >= 0)open:open: () => filterWords(word => word.indexOf('X') >= 0),
0赞 Alexander Farber 10/14/2023
是的,这就是我想要的:我的回报结束了filterWords
1赞 Mark Schultheiss 10/14/2023
Open 事件处理程序具有这种形式,也许在那个匿名的函数中调用您的函数 - 注意只是为了保持一致性 ref: api.jqueryui.com/dialog/#event-open 请注意,您正在使用但未将其传递给您的函数open: function( event, ui ) {}uithis
0赞 Alexander Farber 10/14/2023
我知道,这就是我当前的代码正在做的事情......但是现在我正在尝试将其更改为使用一个方法(称为)返回一个闭包,并带有一个条件(一个 2 个字母的单词;一个包含“X”的单词等)嵌入其中。filterWordsfunction( event, ui ) {}
1赞 Mark Schultheiss 10/14/2023
给我一分钟时间,用你的一些代码来总结一个工作示例

答:

1赞 Mark Schultheiss 10/14/2023 #1

这很粗糙,但在这里我创建了一个 UI 小部件方法,并将其添加到其中,然后展示了如何使用按钮调用它。filterWordsui.dialog

或者,我添加了一个在打开对话框时调用的方法。

笔记:

  • 对话框还会触发打开filterWords
  • 注释掉了你在这个演示中的很多代码,因为我没有可用的代码。HASHED
  • 添加了一个开放处理程序,如果你想朝那个方向发展
  • 注意小部件代码如何使用this.element
  • 小部件文档:https://jqueryui.com/widget/
  • 注意该对话框是如何可用的,正如我在带有 html 元素日志的 RAW 函数中所示$(this)$(this).find('.words')
  • 我添加了一些额外的代码来展示您的字数统计如何成为一个选项wordcount:2,

可能还有其他方法可以使其更有效率,但在这里,我只是在这里展示了许多可用的选项。

$.widget("ui.dialog", $.ui.dialog, {
  filterWords: function(event) {
    let count = this.options.wordcount;
    console.log(count);
    console.log('we are here!');
    //console.log(this.element);
    let myWords = this.element.find('.words');
    console.log('words:', myWords.length, myWords.html());
    $(this).trigger('open');

    /*
           // the words already filtered and assigned to the dialog's innerHTML
    if ($(this).find('.words').html().length > 1000) {
        return (ev, ui) => {};
    }

    // filter the keys of HASHED dictionary by calling the filterFunc on each
    const filtered = Object.keys(HASHED)
        .filter(word => filterFunc(word))
        .reduce((result, word) => {
            return result + 
                '<p><span class="tile">' + word + '</span> ' + HASHED[word] + '</p>'
        }, '');

    // return the closure expected by the dialog's "open" option
    return (ev, ui) => {
        $(this).html(filtered);
        const title = $(this).dialog('option', 'title');
        console.log(title + ': ' + filtered.length + ' chars');
    };
     */
  }
});


function filterWordsRaw(event, ui) {
  console.log('in RAW form');
  console.log($(this).find('.words').html());
  // the words already filtered and assigned to the dialog's innerHTML
  if ($(this).html().length > 1000) {
    return (ev, ui) => {};
  }
  /* commented out as I do not have HASHED defined 
    // filter the keys of HASHED dictionary by calling the filterFunc on each
    const filtered = Object.keys(HASHED)
      .filter(word => filterFunc(word))
      .reduce((result, word) => {
        return result +
          '<p><span class="tile">' + word + '</span> ' + HASHED[word] + '</p>'
      }, '');

    // return the closure expected by the dialog's "open" option
    return (ev, ui) => {
      $(this).html(filtered);
      const title = $(this).dialog('option', 'title');
      console.log(title + ': ' + filtered.length + ' chars');
    };
    */
}

/* just to show we did this */
function handleOpen(event) {
  console.log('opened');
  console.log(event.target === this);
}

$(".selector").dialog({
  modal: true,
  appendTo: '#fullDiv',
  autoOpen: false,
  open: handleOpen,
  wordcount: 2,
  buttons: {
    'Close': function() {
      $(this).dialog('close');
    }
  }
});

$(".selector").on("dialogopen", filterWordsRaw);

$('.go-do-it').on('click', function(event) {
  console.log('hi');
  $(".selector").dialog("filterWords");
});
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/redmond/jquery-ui.css">
<div class="container">
  <button type="button" class="go-do-it">Click</button>
</div>

<div class="selector" title="Dialog Title">
  <span class="words">one fish two fish red fish blue fish</span>
</div>
<div id="fullDiv"></div>

评论

0赞 Mark Schultheiss 10/14/2023
不是真正完整的功能,但更多的是说明如何扩展UI对话框并使用与jQuery和jQuery UI小部件相关的一些内容
0赞 Mark Schultheiss 10/14/2023
注意,在对话框扩展中,我破解了 togiether,您将作为$(this).html(filtered);this.element.html(filtered);
0赞 Alexander Farber 10/15/2023
马克,谢谢你的努力,我喜欢你个人资料页面上的座右铭!但是,您建议的修复方法对我来说很难理解。所以我准备了我自己的 jsFiddle,里面有 3 个对话框和 3 个按钮可以打开。该方法有效,但重复代码过多。注释掉的方法失败。我也会用演示来更新我的问题。filterWordsWorks()open: filterWordsBroken(word => word.length == 2),
1赞 Mark Schultheiss 10/15/2023 #2

尽可能多地使用。

这不是对问题的直接回答,而更像是您既定目标的 x/y 示例:更少的代码

由于您表示您的愿望是更少的代码,因此这里有一个更简单的示例。

  • 在按钮和对话框中使用一些数据属性

    • 请注意,对话框 3 没有,并且使用代码默认值wordLength: 3,
  • 对所有按钮使用一个类,以便在代码中将它们作为目标

  • 对所有对话框使用类,以便在代码中将它们作为目标

  • 按钮上的 Data 属性,用于说明它指向哪个对话框。

  • 例如,使用了一些文字(带有反引号)

    `${myvar}text more text${anothervar} fun`;
    
  • 从按钮单击中删除了 ,而是使用了 on 元素preventDefaulttype="button"

  • 链接 和 - 顺序很重要,因为它不返回 jQuery,但事件处理程序会返回。$('.my-letter-dialog') .on("dialogopen".dialog(.dialog(

您可以从一个对话框创建一个对话框 - 为此添加了一个“5”字母示例,但它可能是所有对话框。

'use strict';

const HASHED = {
  "one": "Word description 1",
  "two": "Word description 2",
  "three": "Word description 3",
  "four": "Word description 4",
  "five": "Word description 5",
  "six": "Word description 6",
  "seven": "Word description 7"
};
jQuery(function($) {
  function filterFunc(word, len) {
    //console.log(word, len, word.length == len);
    return word.length == len;
  }
  $('.my-letter-dialog')
    .on("dialogopen", function(event, ui) {
      let elWLen = $(this).data('wordlength');
      let wordLen = !!elWLen ? elWLen : $(this).dialog("option", "wordLength");
      console.log(wordLen);
      if ($(this).html().length > 1000) {
        return (ev, ui) => {};
      }
      const filtered = Object.keys(HASHED)
        .filter(word => filterFunc(word, wordLen))
        .reduce((result, word) => {
          return `${result}<p>${word}:${HASHED[word]}</p>`
        }, '');
      //console.log('filtered:', filtered)
      $(this).html(filtered);
      const title = $(this).dialog('option', 'title');
      const newTitle = `${title}:${filtered.length} chars`;
      $(this).dialog('option', 'title', newTitle);
    })
    .dialog({
      modal: true,
      autoOpen: false,
      wordLength: 3,
      filterFunc: filterFunc
    });

  $('.my-letter-button').button()
    .on("click", function(ev) {
      const targ = $(this).data('targetselector');
      $(targ).dialog('open');
    });
});
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-ui@1/dist/themes/redmond/jquery-ui.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ui@1/dist/jquery-ui.min.js"></script>

<!-- beware: the twoDlg will be empty -->
<div class="my-letter-dialog" id="twoDlg" data-wordlength="2" title="2 letters"></div>
<div class="my-letter-dialog" id="threeDlg" title="3 letters"></div>
<div class="my-letter-dialog" id="fourDlg" data-wordlength="4" title="4 letters"></div>

<button class="my-letter-button" type="button" id="twoBtn" data-targetselector="#twoDlg">2 letters</button>
<button class="my-letter-button" type="button" id="threeBtn" data-targetselector="#threeDlg">3 letters</button>
<button class="my-letter-button" type="button" id="fourBtn" data-targetselector="#fourDlg">4 letters</button>

评论

0赞 Alexander Farber 10/15/2023
你认为可以通过以下方式传递比较函数吗?或者通过附加到对话框的一些上下文对象 t 其创建时间?因为我不仅看单词长度,而且在我的真实游戏中还有 2 个按钮......$(this).dataword=>word.indexOf('Q')>=0word=>word.indexOf('X')>=0
1赞 Mark Schultheiss 10/16/2023
确定你的上下文是什么,以及它是否可以通过 - 我最初提出扩展选项的原因之一是你也许可以在这里利用它来发挥你的优势 - 不要因为感知到的复杂性而气馁,因为理解这在很大程度上是关于 JavaScript 和这里实例的“上下文”。查看此 stackoverflow.com/questions/9057816/... 以及 Hubert 在这里发表的其他一些帖子,因为了解 jQuery UI 使用的 jQuery 背后的基础知识会有所帮助this
1赞 Mark Schultheiss 10/16/2023
当然,你也可以在这里查找Jon Resig的链接,从他2006年上大学时发布初始版本时开始,对jQuery的其他见解。