I see, yes. They werenât like that in the original code. It seems like they were converted somehow. I tested it and it seems like when I paste the code into the browser search bar and copy that (which is how I was removing line breaks and tabs for the bookmarklet, then copy that and paste it somewhere, the quotes are made curly. It depends where I paste it though.
I updated the code to use a popup like you suggested (I even styled it to look decent and use iNatâs theme). It currently displays up to the top 20 results, which should be sufficient, but I can always add UI for pagination. It seems to work pretty well and be user-friendly from my basic tests, but you can let me know what you think.
As a side note, is there any scenario where it would be good to display both common and scientific name? I think there was one case where two items in the list had the same (common) name.
Begin edit
I improved the code more after originally posting this reply.
Bookmarklet usage guide:
- Click the bookmarklet, which should bring up a popup where your cursor is focused on the search field, so you can start typing right away.
- Type a taxon ID, partial/full common or scientific name, and press enter (or click âSearchâ).
- Examples of what you can enter include: 144815, Black-capped Chickadee, Black-capped, Poecile atricapillus, Aves, Birds, Gulls, Mushrooms, Bugs.
- iNatâs API works fine with all the above. Inputs like âmushroomsâ and âbugsâ will have top results of âFungi Including Lichensâ and âInsectsâ respectively.
- Your keyboard will now be focused on the Submit button, so either press enter/click if the name above looks correct, or change it in that above dropdown menu.
- You can also hit Cancel to close the menu and cancel it. Additionally, you can change your search query in the search box (just hit âSearchâ/enter again afterwards).
I implemented a basic check to follow the API rate limit of 60 requests/minute (or 1/sec) that hopefully doesnât let you spam âSubmit.â
Also, I decided to just put all the HTML in a string and append it to the body rather than using document.createElement(), .appendChild(), and setting styles/attributes with JS.
End edit
Here is all the code:
Copy-paste this for the bookmarklet
document.body.insertAdjacentHTML('beforeend', '<div class="bootstrap" style="width: 100%; height: 100%; background-color: rgba(51, 51, 51, 0.553); z-index: 1001; position: fixed; top: 0px; left: 0px;"> <div style="box-sizing: border-box; padding: 2rem; width: 40%; background-color: rgb(238, 238, 238); z-index: 1002; position: fixed; top: 40%; left: 30%; border-radius: 10px; box-shadow: rgba(0, 0, 0, 0.176) 0px 6px 12px;display:flex;flex-direction: column;gap:1rem;"> <form id="sans_taxon_search_form" style="flex:1;"> <h2 style="margin-top: 0px; margin-bottom: 1rem;">Exclude Taxon</h2> <input type="search" name="taxon" class="form-control" placeholder="Taxon ID or partial/full name" style="width: 70%;display: inline-block;margin-right:0.35rem;" autocomplete="off"> <input type="submit" name="submit" class="btn btn-default" value="Search"> </form> <form id="sans_taxon_sel_form" style="flex:1;"> <select class="form-control" name="taxon_id"> <option value="" disabled selected>-- Search Something --</option> </select> <button class="btn btn-success" style="margin-top: 0.5rem;" type="submit" name="submit" disabled>Submit</button> <button class="btn btn-default" style="margin-top: 0.5rem; margin-left: 0.35rem" name="cancel">Cancel</button> <p style="margin-top: 0.5rem;"><i>You can exclude multiple taxa by using this bookmarklet again after clicking "Submit"</i></p> </form> </div> </div>'); var srch_form = document.getElementById("sans_taxon_search_form"); var sel_form = document.getElementById("sans_taxon_sel_form"); var last_submitted_time, taxon_id; var btn_submit = sel_form.submit; var btn_cancel = sel_form.cancel; sel_form.addEventListener("submit", function(e) { e.preventDefault(); var taxon_id = e.target.taxon_id.value; var current_without = u.searchParams.get("without_taxon_id"); if (current_without == null) { u.searchParams.set("without_taxon_id", taxon_id); } else { u.searchParams.set("without_taxon_id", `${current_without},${taxon_id}`); } window.location.href = u.href; }); btn_cancel.addEventListener("click", function(e) { sel_form.parentElement.parentElement.remove(); }); srch_form.taxon.focus(); srch_form.addEventListener("submit", function(e) { e.preventDefault(); var time = e.timeStamp; if ((time - (last_submitted_time ?? 0)) >= 1000) { last_submitted_time = time; var form = e.target; var taxon = form.taxon.value; if (taxon != null && taxon.trim() != "") { u = new URL(window.location.href); if (isNaN(taxon)) { fetch(`https://api.inaturalist.org/v1/taxa/autocomplete?q=${taxon}&per_page=20`).then((r) => { r.json().then((j) => { var select = sel_form.taxon_id; if (j.total_results == 0) { select.options[0].innerHTML = "No results"; sel_form.submit.disabled = true; } else { select.innerHTML = ""; for (const result of j.results) { var opt = document.createElement("option"); opt.innerHTML = result.preferred_common_name ?? (`${result.rank} ${result.name}`); opt.value = result.id; select.appendChild(opt); } sel_form.submit.disabled = false; sel_form.submit.focus(); } }); }); } else { taxon_id = parseInt(taxon); var current_without = u.searchParams.get("without_taxon_id"); u.searchParams.set("without_taxon_id", (current_without == null ? "" : `${current_without},`) + taxon_id); window.location.href = u.href; }; } } else { alert("Slow down!"); } });
Readable:
document.body.insertAdjacentHTML('beforeend', '<div class="bootstrap" style="width: 100%; height: 100%; background-color: rgba(51, 51, 51, 0.553); z-index: 1001; position: fixed; top: 0px; left: 0px;"> <div style="box-sizing: border-box; padding: 2rem; width: 40%; background-color: rgb(238, 238, 238); z-index: 1002; position: fixed; top: 40%; left: 30%; border-radius: 10px; box-shadow: rgba(0, 0, 0, 0.176) 0px 6px 12px;display:flex;flex-direction: column;gap:1rem;"> <form id="sans_taxon_search_form" style="flex:1;"> <h2 style="margin-top: 0px; margin-bottom: 1rem;">Exclude Taxon</h2> <input type="search" name="taxon" class="form-control" placeholder="Taxon ID or partial/full name" style="width: 70%;display: inline-block;margin-right:0.35rem;" autocomplete="off"> <input type="submit" name="submit" class="btn btn-default" value="Search"> </form> <form id="sans_taxon_sel_form" style="flex:1;"> <select class="form-control" name="taxon_id"> <option value="" disabled selected>-- Search Something --</option> </select> <button class="btn btn-success" style="margin-top: 0.5rem;" type="submit" name="submit" disabled>Submit</button> <button class="btn btn-default" style="margin-top: 0.5rem; margin-left: 0.35rem" name="cancel">Cancel</button> <p style="margin-top: 0.5rem;"><i>You can exclude multiple taxa by using this bookmarklet again after clicking "Submit"</i></p> </form> </div> </div>');
var srch_form = document.getElementById("sans_taxon_search_form");
var sel_form = document.getElementById("sans_taxon_sel_form");
var last_submitted_time, taxon_id;
var btn_submit = sel_form.submit;
var btn_cancel = sel_form.cancel;
sel_form.addEventListener("submit", function(e) {
e.preventDefault();
var taxon_id = e.target.taxon_id.value;
var current_without = u.searchParams.get("without_taxon_id");
if (current_without == null) {
u.searchParams.set("without_taxon_id", taxon_id);
} else {
u.searchParams.set("without_taxon_id", `${current_without},${taxon_id}`);
}
window.location.href = u.href;
});
btn_cancel.addEventListener("click", function(e) {
sel_form.parentElement.parentElement.remove();
});
srch_form.taxon.focus();
srch_form.addEventListener("submit", function(e) {
e.preventDefault();
var time = e.timeStamp;
if ((time - (last_submitted_time ?? 0)) >= 1000) {
last_submitted_time = time;
var form = e.target;
var taxon = form.taxon.value;
if (taxon != null && taxon.trim() != "") {
u = new URL(window.location.href);
if (isNaN(taxon)) {
fetch(`https://api.inaturalist.org/v1/taxa/autocomplete?q=${taxon}&per_page=20`).then((r) => {
r.json().then((j) => {
var select = sel_form.taxon_id;
if (j.total_results == 0) {
select.options[0].innerHTML = "No results";
sel_form.submit.disabled = true;
} else {
select.innerHTML = "";
for (const result of j.results) {
var opt = document.createElement("option");
opt.innerHTML = result.preferred_common_name ?? (`${result.rank} ${result.name}`);
opt.value = result.id;
select.appendChild(opt);
}
sel_form.submit.disabled = false;
sel_form.submit.focus();
}
});
});
} else {
taxon_id = parseInt(taxon);
var current_without = u.searchParams.get("without_taxon_id");
u.searchParams.set("without_taxon_id", (current_without == null ? "" : `${current_without},`) + taxon_id);
window.location.href = u.href;
};
}
} else {
alert("Slow down!");
}
});
HTML:
<div class="bootstrap" style="width: 100%; height: 100%; background-color: rgba(51, 51, 51, 0.553); z-index: 1001; position: fixed; top: 0px; left: 0px;">
<div style="box-sizing: border-box; padding: 2rem; width: 40%; background-color: rgb(238, 238, 238); z-index: 1002; position: fixed; top: 40%; left: 30%; border-radius: 10px; box-shadow: rgba(0, 0, 0, 0.176) 0px 6px 12px;display:flex;flex-direction: column;gap:1rem;">
<form id="sans_taxon_search_form" style="flex:1;">
<h2 style="margin-top: 0px; margin-bottom: 1rem;">Exclude Taxon</h2>
<input type="search" name="taxon" class="form-control" placeholder="Taxon ID or partial/full name" style="width: 70%;display: inline-block;margin-right:0.35rem;" autocomplete="off">
<input type="submit" name="submit" class="btn btn-default" value="Search">
</form>
<form id="sans_taxon_sel_form" style="flex:1;">
<select class="form-control" name="taxon_id">
<option value="" disabled selected>-- Search Something --</option>
</select>
<button class="btn btn-success" style="margin-top: 0.5rem;" type="submit" name="submit" disabled>Submit</button>
<button class="btn btn-default" style="margin-top: 0.5rem; margin-left: 0.35rem" name="cancel">Cancel</button>
<p style="margin-top: 0.5rem;"><i>You can exclude multiple taxa by using this bookmarklet again after clicking "Submit"</i></p>
</form>
</div>
</div>