I am trying to cycle through a list of custom autocomplete options using the arrow keys in JavaScript. I am attempting to do this by iterating through the options and adding a "selected" ID to the option currently selected. I've run in to a problem where, although the "selected" ID of the option currently selected is visible (if you log it out, you can see the ID), the ID is inaccessible (trying to log out element.id returns an empty string).
Here is the code:
SearchBox.prototype.handleOptionNavigation = function() {
_this.inputElement.addEventListener("keyup", function(event) {
var options = _this.getOptions();
if (event.key === "ArrowUp") _this.moveSelectedUp();
if (event.key === "ArrowDown") _this.moveSelectedDown();
});
}
SearchBox.prototype.getOptions = function() {
return Array.from(document.getElementsByClassName("result-item"));
}
SearchBox.prototype.getSelectedIndex = function() { //here is the problem
var options = _this.getOptions();
if (options.length === 0) return;
console.log(options[2]);
//this returns <li class="result-item" id="selected">...</li>
console.log(options[2].id);
//this returns an empty string
return 1;
//this function is supposed to return the index of the element currently selected;
//I am returning 1 just to see a selected element on the screen.
}
SearchBox.prototype.moveSelectedDown = function() {
var options = _this.getOptions();
if (options.length === 0) return;
var selectedIndex = _this.getSelectedIndex();
if (selectedIndex === -1) {
options[0].id = "selected"
} else if (selectedIndex === (_this.maxResults - 1)) {
options[0].id = "selected"
options[options.length - 1].removeAttribute("id");
} else {
console.log("we are moving down");
options[selectedIndex + 1].id = "selected";
options[selectedIndex].removeAttribute("id");
}
}
SearchBox.prototype.moveSelectedUp = function() {
var options = _this.getOptions();
if (options.length === 0) return;
var selectedIndex = _this.getSelectedIndex();
console.log(selectedIndex);
if (selectedIndex === -1) {
options[options.length - 1].id = "selected";
} else if (selectedIndex === 0) {
options[0].removeAttribute("id");
} else {
options[selectedIndex - 1].id = "selected";
options[selectedIndex].removeAttribute("id");
}
}
The idea is that, with each press of the up or down arrows, a different element in the list of complete options will become highlighted. However, because I can't seem to access the id of the selected element, it gets stuck and the moveSelectedUp/moveSelectedDown functions don't work.
Does anyone know what is going on here?
Thank you!
Not quite sure what you are trying to achieve in your method SearchBox.prototype.getSelectedIndex, but it is always returning 1, how is that supposed to help ?
I've updated your method so that it returns the real index and it seems to work fine, see the fiddle below :
https://jsfiddle.net/c2p1aayh/
Related
I got a little problem that I can't solve...
I usually check my form like this:
function checkFirst(field) {
if (field.value.length < 2 || !regLetters.test(field.value)) {
//do something
} else {
//do something else
firstNameOk = true;
}
}
and on the HTML side with onblur="checkFirst(this)".
Now I'm using OOP and I can't use my methods in onblur and I don't know how I could call the class in the onblur HTML attribute...
I already got a solution to work around this without using onblur in HTML and in my case I'd like to know if it's possible or not.
Anyone to help me please?
Edit:to avoid people telling me to use addEventlistener i show you my solution that works fine but not the one i wanted to use...
this.data.forEach(item => item.addEventListener('blur', function () {
console.log(item.id)
// check first name field //
if (item.id === "first") {
if (item.value.length < 2 || !this.regLetters.test(item.value)) { // if this field length =
this.highlightField(item, true);
} else {
this.highlightField(item, false);
this.errorMessagesReset(item);
this.firstNameOk = true;
}
// check last name field //
} else if (item.id === "last") {
if (item.value.length < 2 || !this.regLetters.test(item.value)) {
this.highlightField(item, true);
} else {
this.highlightField(item, false);
this.errorMessagesReset(item);
this.lastNameOk = true;
}
// check email field //
} else if (item.id === "email") {
if (item.value.length < 2 || !this.regmail.test(item.value)) {
this.highlightField(item, true);
} else {
this.highlightField(item, false);
this.errorMessagesReset(item);
this.emailOk = true;
}
// check textarea field //
} else if (item.id === "message") {
if (item.value.length < 1 || item.value > 100) { // if length of item is sup or equal to 1 and
this.highlightField(item, true);
} else {
this.highlightField(item, false);
this.errorMessagesReset(item);
this.messageOk = true;
}
}
}.bind(this))); ```
It should be a static function that belongs to the class then. That way you can call it directly.
I am using this function to show and hide objects. I think the reason why this isn't working is because I am not selecting the object correctly.
function generalHideOrShow(element)
{
if (element instanceof Element)
{
//single element passed
element = [element]; //mimic node list
}
if(element.length && element.length > 0 && element[0] instanceof Element)
{
//node list
for (var i = 0; i < element.length; ++i)
{
if (element[i].getAttribute("data-hidden") == "true" )
{
$(element[i]).removeClass("hidden");
element[i].setAttribute("data-hidden", false);
}
else
{
element[i].setAttribute("data-hidden", true);
$(element[i]).addClass("hidden");
}
}
}
else
{
return false;
}
}
d3.selectAll("#button1").on("click", function(){
generalHideOrShow($("#buttonsRight")); //selection
});
var buttons = d3.select("#svg").append("g").attr("id", "buttons");
var buttonsRightTop = buttons.append("g").attr("id", "buttonsRightTop");
var buttonsRight = buttonsRightTop.append("g").attr("id", "buttonsRight");
I wish to select 'buttonsRight' as above.
When I change it to select all 'div' tags to test it, it works.
generalHideOrShow($("div")); //selection
I have tried different ways of selecting it such as :
generalHideOrShow($(buttonsRight)); //selection
generalHideOrShow($(".buttonsRight")); //selection
generalHideOrShow($("g#buttonsRight")); //selection
None are working. How do I select this right side buttons ?
Since you are using jQuery, I think you can write it as
function generalHideOrShow(element) {
var $elem = $(element);
if ($elem.length) {
var $hid = $elem.filter('[data-hidden="true"]').removeClass('hidden').attr("data-hidden", false);
$elem.not($hid).addClass('hidden').attr("data-hidden", true);
} else {
return false;
}
}
This is how I managed to do it:
Call the generalHideOrShow Function with the onClick:
d3.select("thisButton").on("click", function(){
generalHideOrShow("#buttonsRight");
}
set the class to visible first so you can check the class later:
buttonsRight.classed("visible", true);
Then do if statements to check if the class is hidden or visible
function generalHideOrShow(element) {
console.log(element[0].getAttribute('class'));
if(element[0].getAttribute('class') === "visible"){
element[0].setAttribute('class', "hidden");
} else{
element[0].setAttribute('class', "visible");
}
I need to detect changes on page change in page input field of pagingtoolbar in extjs 4.2.
I am going through docs, but can't find any method for it. I have successfully overridden next, previous, &c., buttons, but can't find anything to override page input field. How would you go about this?
I have added afterrender listener on ExtJS Combobox component. You can add accordingly to override input field of paging toolbar. Here is the working code :
'afterrender' : function(thisCombo){
thisCombo.getPicker().pagingToolbar.addListener('change', function() {
var me = this;
thisCombo.getPicker().pagingToolbar.child("#inputItem").addListener('specialkey', function(field, e) {
if (e.getKey() == e.ENTER) {
///// Do your modifications here
var inputItem = thisCombo.getPicker().pagingToolbar.child('#inputItem').getValue();
total = me.getPageData().pageCount;
if (inputItem <= total) {
if (me.fireEvent('beforechange', me, inputItem) !== false) {
me.store.inputItemPage({
// Enter params
});
}
}
}
});
});
}
}
this example my help
As in the code you can all of the texts given that they have setter etc
http://jsfiddle.net/acteon/sZ3y6/1/
Ext.toolbar.Paging.override({
onLoad: function () {
var me = this,
pageData, currPage, pageCount, afterText, count, isEmpty;
count = me.store.getCount();
isEmpty = count === 0;
if (!isEmpty) {
pageData = me.getPageData();
currPage = pageData.currentPage;
pageCount = pageData.pageCount;
afterText = Ext.String.format(me.afterPageText, (isNaN(pageCount) || (pageCount === 0)) ? 1 : pageCount);
} else {
currPage = 0;
pageCount = 0;
afterText = Ext.String.format(me.afterPageText, 1);
}
Ext.suspendLayouts();
me.child('#afterTextItem').setText("my precious text");
// this one is the input field
me.child('#inputItem').setDisabled(isEmpty).setValue(currPage);
me.child('#first').setDisabled(currPage === 1 || isEmpty);
me.child('#prev').setDisabled(currPage === 1 || isEmpty);
me.child('#next').setDisabled(currPage === pageCount || isEmpty);
me.child('#last').setDisabled(currPage === pageCount || isEmpty);
me.child('#refresh').enable();
me.updateInfo();
Ext.resumeLayouts(false);
if (me.rendered) {
me.fireEvent('change', me, pageData);
}
}
});
thanks for your help.
I have another issue, #inputItem field. What event handles the enter/return key? I need to override this function, because I have a disable/enable button.
In my JSON file I have 7 objects, where first 3 of them have "is_read" attribute == 1, and last 4 have "is_read" == 0.
I add rows, using a template and want to give tr different classes according to their "is_read" value (".news_read" for "is_read" == 1 and ".news_unread" for "is_read" == 0).
However, I end up with 7 rows that all have "news_unread" class. Though, console.log shows that I have 3 "newsData.get('is_read') == 1" and 4 "newsData.get('is_read') == 0" objects.
I wonder how to create rows with different classes. I tried to do newsRow.addClass, but the error message says that an object <tr><td>...</td></tr> (newsRow template) can't have a method addClass.
render: function() {
news.fetchMyNews();
for (var i = 1; i <= news.length; i++) {
var newsData = news.get(i);
var newsRow = JST["news/row"](newsData.attributes);
$("#news_tbody").append(newsRow).first('tr').attr("class",function(){
if (newsData.get('is_read') == 1)
return "news_read";
else if (newsData.get('is_read') == 0)
return "news_unread";
});
}
}
You wrote:
I tried to do newsRow.addClass, but the error message says that an object ... (newsRow template) can't have a method addClass.
But I can't find addClass in your example code:
$("#news_tbody").append(newsRow).first('tr').attr("class",function(){
if (newsData.get('is_read') == 1)
return "news_read";
else if (newsData.get('is_read') == 0)
return "news_unread";
});
I just can advice you to try this code(use addClass, instead of attr and add blocks in if statement):
$("#news_tbody").append(newsRow).first('tr').addClass(function(){
if (newsData.get('is_read') === 1){
return "news_read";
} else if (newsData.get('is_read') === 0) {
return "news_unread";
}
});
As #Pinal suggested, I used addClass instead.
However, after append(newsRow) I used .children('tr') and it worked fine.
$("#news_tbody").append(newsRow).children('tr').addClass(function(){
if (newsData.get('is_read') === 1){
return "news_read";
} else if (newsData.get('is_read') === 0) {
return "news_unread";
}
});
I have a search suggestion div that appears when you keyUp an input. This works fine, but now I am trying to make keyboard shortcuts in action.
I want a behavior like when you click down keyboard arrow button a span gets selected and if it is selected then another span that is after gets selected, similarly, if you click up arrow an upward span gets selected, when you click enter then link opens.
I am stuck because I could not remove a:hover and could not add classes to it. Even after I have basically no idea how to do it. But I really tried hard and a lot..
Here is a jsfiddle link (type anything in field). maybe somebody will help me.
This code should go when the request is made and data is being returned:
<script type="text/javascript">
$(document).ready(function(){
total = 3;
$(".result-item").mouseenter(
function(){
hovered = $(this).attr("id");
total = 3;
$(".result-item").each(function(){
$(this).children("a").css({
'background-color':'#e4e4e4',
'color':'#000000'
});
$(this).find(".searchheading").css({
'color':'#191919'
});
$(this).find(".searchcaption").css({
'color':'#555555'
});
});
$(this).children("a").css({
'background-color':'#b7b7b7',
'color':'#ffffff'
});
$(this).find(".searchheading").css({
'color':'#ffffff'
});
$(this).find(".searchcaption").css({
'color':'#f1f1f1'
});
}
);
});
</script>
And this code on a page where request is made:
$("#suggestions").hide();
$("#search").bind('keyup', function(event){
if (event.which == 40 || event.which == 38 || event.which == 13) return false;
else
{
hovered = undefined;
lookup($(this).val());
}
});
$("#search").bind('keydown', 'down', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
$("#result-item-0").trigger("mouseenter");
return;
}
count = parseInt($("#"+hovered).attr("count"));
next = (count + 1);
if (next == total)
next = 0;
$("#result-item-"+next).trigger("mouseenter");
}
});
$("#search").bind('keydown', 'up', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
$("#result-item-"+(total-1)).trigger("mouseenter");
return;
}
count = parseInt($("#"+hovered).attr("count"));
prev = (count - 1);
if (prev == -1)
prev = (total-1);
$("#result-item-"+prev).trigger("mouseenter");
}
});
$("#search").bind('keydown', 'return', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
str = $("#search").val();
window.location.href = urlencode(str); // urlencode is a custom function
return false;
}
count = parseInt($("#"+hovered).attr("count"));
current = count;
$("#result-item-"+current).trigger("mouseenter");
$("#suggestions").fadeOut();
window.location.href = $("#"+hovered).children("a").attr("href");
}
});
})
;
Also I removed onkeyup="" attribute on element, this approach is nicer.