{"id":13518220,"url":"https://github.com/basecamp/lufo","last_synced_at":"2025-03-31T09:31:21.543Z","repository":{"id":65987458,"uuid":"54924208","full_name":"basecamp/lufo","owner":"basecamp","description":"Tracks the most recent options chosen on a `\u003cselect\u003e` element and displays them at the top of the list","archived":true,"fork":false,"pushed_at":"2016-07-28T19:17:04.000Z","size":4609,"stargazers_count":87,"open_issues_count":0,"forks_count":3,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-03-25T05:55:53.198Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/basecamp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-28T21:15:45.000Z","updated_at":"2024-09-15T18:02:37.000Z","dependencies_parsed_at":"2023-02-19T20:45:53.550Z","dependency_job_id":null,"html_url":"https://github.com/basecamp/lufo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Flufo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Flufo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Flufo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Flufo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/basecamp","download_url":"https://codeload.github.com/basecamp/lufo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246446882,"owners_count":20778890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T05:01:42.277Z","updated_at":"2025-03-31T09:31:20.695Z","avatar_url":"https://github.com/basecamp.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"### Lufo for jQuery\n\n**A jQuery plugin to track the most recent options chosen on a `\u003cselect\u003e` element and display them at the top of the list**\n\n-----------\n\n**“Stop being regionally biased”**\n\nThat’s the subject of an email we received recently at [**Highrise**](https://highrisehq.com/). \n\nWell that got our intention. What are we doing wrong?\n\nThe message explained: \n\n\u003e Your current drop down menu for country locations is not very Asia friendly - all those listed at the top of the list are in North America, Europe + Japan. My most frequent country selections are Hong Kong, Singapore, Indonesia, Vietnam, China, etc...\n\nThey were right. Our menu is 200 countries long, and we highlight a few, but it’s clearly biased towards that few. \n\nBut that got us thinking... this isn’t just a problem for Countries, we have this problem all over our app. We have a bunch of menus, many with more than a few choices, where it’s hard to repeatedly pick things that aren’t at the top of the list. \n\nSure, browsers try and give you the ability to type some letters to find things faster in the menu, but they fail at anything but rudimentary searches. \n\nWhat we really need is a solution to have HTML select menus remember what the user last picked and make those easier to pick next time. \n\nSomething like: \n\n**L**ast\n**U**sed\n**F**irst\n**O**ut\n\nLUFO!!!\n\n----------------------\n\nLong `\u003cselect\u003e` menus with many options can be a pain. States/territories, countries, currency lists, etc. have so many options it’s cumbersome to scroll through them to find the ones you use often. \n\nLufo tracks the most recent selections on a long `\u003cselect\u003e` menu and stores them in a browser’s `localStorage` (it falls back to a tracking cookie if the browser doesn’t support `localStorage`). When someone revisits a page with that same long menu, their most-recently selected options will be copied to the top of the list for their convenience. No bias on your part. Super-convenient for the people using your site.\n\nYou can add Lufo to just about any `\u003cselect\u003e` form control, choose to group the recent selects, customize (or hide) the labels and divider text, and even enable/disable Lufo based on how many options are in a dynamically-generated `\u003cselect\u003e`.\n\n\u003cimg src=\"https://github.com/highrisehq/lufo/blob/master/demo.gif?raw=true\" width=500 /\u003e\n\n## Usage\n\n1. Include [jQuery](http://jquery.com) in your project\n2. Include the Lufo plugin code:\n\n  ```html\n  \u003cscript src=\"jquery.lufo.js\"\u003e\u003c/script\u003e\n  ```\n\n3. Call the Lufo plugin on a `\u003cselect\u003e` element:\n\n  ```javascript\n  $('select').lufo();\n  ```\n  \n  If you are using the plugin with multiple, _different_, `\u003cselect\u003e` menus, you will want to specify a unique storage name for each menu:\n  \n  ```javascript\n  $('select.some-select').lufo({\n    listStoreName: 'someSelectValues'\n  });\n  ```\n  \n  [More on Storage \u0026 Tracking Cookie Preferences.](#storage-tracking-cookie-preferences) See below for other available options.\n\n## Options\n\nUnless otherwise noted, all option examples below are Lufo defaults.\n\n#### Check for Initial Placeholder Values\nIf your `\u003cselect\u003e` menu has a built-in placeholder (for example: “Select a country…”) that resides at the top of the list and has no value, Lufo will check for it, and if found, move it to the very top of the list _after_ adding in the most-recently selected options.\n\n```javascript\ncheckInitialValue: true\n```\n\nSet to `false` to turn off this check.\n\n#### Strip “selected” from Cloned Values\nIf a recent option has the selected attribute set (`selected=\"selected\"`) it will remain when that `\u003coption\u003e` is copied to the top of the list.\n\n```javascript\nstripSelected: false\n```\n\nSet `stripSelected` to `true` if you want the selected attribute removed from the copy.\n\n#### A Title for the Recently Selected List\nBy default, Lufo will place a disabled `\u003coption\u003e` at the beginning of the most-recently selected options list with the text: “Recently selected:”.\n\n```javascript\nrecentsListTitleEnabled: true,\nrecentsListTitle: 'Recently selected:'\n```\n\nTurn the title off by setting `recentsListTitleEnabled` to `false` or change the title text by setting `recentsListTitle` to something else.\n\n#### List Divider\nLufo will place a disabled `\u003coption\u003e` at the end of the most-recently selected options list to use as a divider.\n\n```javascript\ndividerEnabled: true\ndividerText: '––––––––––––––––––––––––'\n```\n\nYou may disable the divider by setting `dividerEnabled` to `false` or modify the divider by setting `dividerText` to something else.\n\n#### Recently Selected List Memory\nBy default, Lufo will remember the `5` most-recently selected items in a `\u003cselect\u003e` menu.\n\n```javascript\nrecentsListLength: 5\n```\n\nYou may change this number to any positive integer.\n\n#### Conditionally Enabling Lufo\nA `\u003cselect\u003e` list must have at least `5` items in it to enable the tracking and display of recently selected items. This is especially useful for dynamic lists in an app that may need to grow over time before it becomes useful to track recent selects.\n\n```javascript\nlistMinimumLength: 5\n```\n\nYou may change this number to any positive integer to enable Lufo sooner, or to wait until a list grows beyond five options.\n\n#### Using `\u003coptgroup\u003e`\nBy default, Lufo adds the list of recent selects to the top-level of a `\u003cselect\u003e` menu.\n\n```javascript\ngroupList: false\n```\n\nIf you wish to group the options into an `\u003coptgroup\u003e` set `groupList` to `true`.\n\n#### Ignoring Certain Values\nLufo will track clicks on _all_ available `\u003coption\u003e` in a `\u003cselect\u003e`.\n\nIf you wish to ignore a few options in the list, you may create an array of the _values_ (`value=`) that you do not want Lufo to track.\n\n**Example:**\n\n```javascript\nignoredValues: ['dog', 'cat', 'cow']\n```\n\nSetting the above option would not track clicks on any options in a `\u003cselect\u003e` that contain the values of `dog`, `cat`, or `cow`.\n\n#### Storage \u0026 Tracking Cookie Preferences\nIf you will be using Lufo on a single `\u003cselect\u003e` menu on your site, you don’t need to change any of the storage preferences. Lufo will use a browser’s `localStorage` (or if that isn’t available, set a tracking cookie) to remember the most recent selections.\n\n**Default:**\n\n```javascript\nlistStoreName: 'recentOptionValues',\ncookieAge: 30 // only relevante for browsers without `localStorage`\n```\n\nHowever, if you are using Lufo on multiple menus with different values, you need to set a unique storage item name for each of the menus.\n\n**Example:**\n\n```javascript\n$('select.some-select').lufo({\n  listStoreName: 'someSelectValues'\n});\n\n$('select.other-select').lufo({\n  listStoreName: 'otherSelectValues'\n});\n```\n\nYou may also increase or decrease the amount of time the tracking cookie persists on your site (for browsers without `localStorage`). The default cookie age is `30` days. You may change it by setting `cookieAge` to another positive integer to represent a number of _days_.\n\n**Example:**\n\n```javascript\ncookieAge: 90\n```\n\nThe example above would remember the recently selected options for `90` days instead of the default `30`.\n\n## Examples\n\n#### Example #1\nSet Lufo to track a `\u003cselect\u003e` with the class name `countries` and set a unique storage item name based on that class name.\n\n```javascript\n$('select.countries').lufo({\n  listStoreName: 'countriesSelectValues'\n});\n```\n\n#### Example #2\nSet Lufo to track a `\u003cselect\u003e` with the ID `author_id`, and a unique storage item name. Only enable Lufo if the list grows to `10` items. Only show `3` of the most recently selected items. Ignore a value of `none`. Finally, turn off the divider, shorten the recent list title, and move the recent selects into an `\u003coptgroup`\u003e.\n\n```javascript\n$('#author_id').lufo({\n  groupList: true,\n  recentsListTitle: 'Recent',\n  dividerEnabled: false,\n  recentsListLength: 3,\n  listMinimumLength: 10,\n  ignoredValues: ['none'],\n  listStoreName: 'authorIdSelectValues'\n});\n```\n\n## Contributing\n\nWe would love to see your contributions to Lufo! Check [CONTRIBUTING.md](https://github.com/highrisehq/lufo/blob/master/CONTRIBUTING.md) for more information.\n\n## License\n\n[MIT License](https://github.com/highrisehq/lufo/blob/master/LICENSE.md)\n\n## Credit\n\nA ton of thanks to [Grant Blakeman](https://twitter.com/gblakeman) doing the vast majority of the work putting this together.\n\n## P.S.\n\n[You should follow us on Twitter: **here**](https://twitter.com/highrise), or see how we can help you with contact management using [**Highrise**](https://highrisehq.com)  —  a handy tool to help you remove anxiety around tracking who to follow up with and what to do next.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasecamp%2Flufo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbasecamp%2Flufo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasecamp%2Flufo/lists"}