Detect span sibling of input type search

I have multiple input type search fields on my page, and Im trying to detect which one is being typed in so i can activate a cross to delete the text. But for the life of me I cant seem to make it happen.

.clearBtn {
    position: absolute;
    margin: 4px 0 0 -18px;
    display: none;
    /*transition: opacity 0.2s;*/
    color: black;
    cursor: pointer;
    z-index: 90;
}

@Html.TextBox("txtSearch", null, new Dictionary<string, object> { { @"class", "textBox textBoxSizeStandard textSizeSmall" }, { "data-column", "all" }, { "type", "search" } })
<span id="clearInputText" class="clearBtn">x</span>

$("input[type='search']").keyup(function (e) {
    $(e.currentTarget).parent().next(".clearBtn").css("display", "inline");
});

$(".clearBtn").click(function () {
    $(this).prev('.textBox').val('');
    $(".clearBtn").hide();
});

This is adding the span dynamically

function ApplySearchCancelOption() {
   var input = $("input[type='search']").each(function () {
         $(this).parent().append("<span id='clearInputText' class='clearBtn'>x</span>");
   });
}

OK looks like I got itā€¦

    $("input[type='search']").keyup(function (e) {
        $(e.currentTarget).next(".clearBtn").css("display", "inline");
    });

This line

$(this).parent().append("<span id='clearInputText' class='clearBtn'>x</span>");

appends the button next to the input element, so youā€™ll have to select it using just .next(), not .parent().next():

$(e.currentTarget).next(".clearBtn").css("display", "inline");

(Or if itā€™s not an immediate sibling:)

$(e.currentTarget).nextAll(".clearBtn").first().css("display", "inline");
2 Likes

Thanks m3g4p0p,

One other issue I have been looking at is this detects if someone types, but I need to add an OR to the keyup function just in case there text in there after they refresh, and if there is the cross appears.

So show cross if typing, or show cross if there text in there after they refresh

When appending that element, you might toggle() its visibility depending on whether the input value is an empty string or not:

$("input[type='search']").each(function () {
  $(this)
    .parent()
    .append("<span id='clearInputText' class='clearBtn'>x</span>")
    .toggle(this.value !== "")
})

Ye I see what your saying, but that append function is called as the tables are being built, so there a sequence to it, and so they append from the build.

I think Iā€™m going to need to keep that append function as it is, and just detect keyups and if there text in there on refresh.

Didnā€™t my example in your last thread do what you wanted? It seems to be what you are asking again?

Oh god sorry Paul,

The weekend meant I lost my thread with it allā€¦

Iā€™ll go back and check sorry

No worrries Iā€™m sure @m3g4p0p can do it better anyway as my jquery is pretty basic :slight_smile:

Its almost there in honesty, its just I need the cross to appear if the user types and then refreshes and the cross appears for them, otherwise its perfect.

Another thing I noticed is if they start typing, and then delete it themselves using the delete button, when the last character has gone the cross stays there.

Something like this, which seems to work

$("input[type='search']").keyup(function (e) {
        $(e.currentTarget).next(".clearBtn").show();
        if ($(e.currentTarget).val() == "") {
            $(e.currentTarget).next(".clearBtn").hide();
        } 
    });

Something like this Iā€™m trying but to no avail

    $(window).on('load', function () {
        if ($("input[type='search']").val() != "") {
            $(this).next(".clearBtn").show();
        }
    });

You need to iterate over the input elements using .each(), otherwise youā€™ll just get the value of the first input; also note that this would refer to the window here.

I have to say I donā€™t really see why you canā€™t do the check while appending those clear-elements thoughā€¦ do you dynamically fill the input values with JS? Otherwise, any prefilled values would be present as soon as the input elements got rendered.

Ye Iā€™ll go back and follow you as Iā€™m getting confused a bit now, the table build was done by someone else which I think has to stay, and in there you will see the ā€˜ApplySearchCancelOption()ā€™ which is calling the append function.

$(document).ready(function () {

        // #region tblUsers

        $('#tblUsers')
            .tablesorter({
                theme: 'bootstrap',
                widthFixed: true,
                headerTemplate: '{content} {icon}',
                widgets: ['uitheme', 'filter', 'columns', 'zebra'],
                widgetOptions: {
                    filter_external: '#txtSearch',
                    filter_cssFilter: 'textBox textSizeSmall',
                    filter_saveFilters: true
                },
                sortList: [[0, 0]]
            })
            .tablesorterPager({ container: $('#pager') })
            .bind('filterEnd', function (event, data) {
                $('#lblCountRecords').text("@LH.GetText(LH.SystemComponent.TheSystem, "lblRecordsCount")" + " " + data.filteredRows);
            });


        $('#tblUsers td[data-column="0"] > input').attr('data-datatype', "text");
        $('#tblUsers td[data-column="1"] > input').attr('data-datatype', "text");
        $('#tblUsers td[data-column="2"] > input').attr('data-datatype', "text");
        $('#tblUsers td[data-column="3"] > input').attr('data-datatype', "multipleChoiceAnyAll");

        ApplySearchCancelOption();
        // #endregion

        // #region txtSearch

        $('#txtSearch').on('input', function () {
            tblUsersHighlight();
        });

        // #endregion

        tblUsersPopulate();

    });

So with this the span appends when the table is being built.

I can see them, they do append fine, so then this is the rest of the jquery below, to listen etc.

// #region add span to input type search

    function ApplySearchCancelOption() {
        var input = $("input[type='search']").each(function () {
            $(this).parent().append("<span class='clearBtn'>x</span>");
        });
    }

    // #endregion

    // #region x clear in input

    $(".clearBtn").click(function () {
        $(this).prev('.textBox').val('');
        $(".clearBtn").hide();
    });

    // #endregion

    // #region show delete x if field typed in

    $("input[type='search']").keyup(function () {
        //alert("fr");
        $("input[type='search']").each(function () {
            $(this).next(".clearBtn").show();
            if ($(this).val() == "") {
                $(this).next(".clearBtn").hide();
            }
        });
    });

    $(document).ready(function () {
        $("input[type='search']").each(function (index) {
            if ($(this).val().length != 0) {
                $(this).next(".clearBtn").show();
            }
        });
    });

    // #endregion

With the way i currently have it, all the above the checking on refresh now works. But what do yout hink of the way it all is, because I think you are right in that it possibly want know what search box is the one in use etc

Well as you probably well aware of all the above doesnā€™t work. As I type in each of the fields, the cross doesnt appear.

Why I thought it was working is because in one of the text boxes on the page I hard coded the span in myself, and was testing that one, and all works fine there.

it doesnt work however with the ones that are appended when the table is created.

Added the each to this below and with the dynamically appended spans, it doesnā€™t recognise the keyupā€™s within them

$("input[type='search']").keyup(function () {
        //alert("fr");
        $("input[type='search']").each(function () {
            $(this).next(".clearBtn").show();
            if ($(this).val() == "") {
                $(this).next(".clearBtn").hide();
            }
        });
    });

Okay so if those search fields are getting created dynamically, you might use event delegation; you can then also append the spans on the fly where necessary:

// Listen to keyboard events on the search fields
$(document).on('keyup', 'input[type="search"]', function () {
  var $next = $(this).next()

  // Create a clear button if it does not exist
  var $clearBtn = $next.is('.clearBtn') 
    ? $next 
    : $('<span class="clearBtn">x</span>').insertAfter(this)
  
  // Toggle that button depending on the input value
  $clearBtn.toggle(this.value !== '')
})

// Similarly, listen to click events for dynamically
// created clear buttons
$(document).on('click', '.clearBtn', function () {
  // Hide the button and clear the search field
  $(this).hide().prev('input[type="search"]').val('')
})

Checking for initial values of the search field will be more difficult though. Does that script that generates the table and search fields provide any sort of hooks for when it creates those elements?

2 Likes

wow, how do you guys know all this stuff lol, thatā€™s pretty cool fair play.

There one issue and thatā€™s with the search field that has the span hard coded if I click the clear x the table contents return to its on load state where everything shows fine, if I though click x on the dynamically appended ones it doesnt allow the table to return to full view until i actually click in the empty text box and then click delete, its as if there something left in there.

Hard to explain, but basically if i type in a search and delete manually using the keyboard delete button once the last character is deleted the form returns to full view. But if I click x to clear the text field, its as if something is still left in there as the table doesnā€™t return to on load view.

But I cant say thank you enough for helping with this, its awesome thank you

Also and I feel terrible pointing it out, if there text in the boxes and the user refreshes the clear cross doesnt appear.

So I added this back and it works for the hard coded one, but not for the dynamic ones

    $(document).ready(function () {
        $("input[type='search']").each(function (index) {
            if ($(this).val().length != 0) {
                $(this).next(".clearBtn").show();
            }
        });
    });

Well thatā€™s hard to diagnose without seeing the actual appā€¦ but Iā€™d assume that table library listens to certain events on the search fields to determine when to return to full view. You can inspect the input element in the dev tools and switch to the ā€œEvent Listenersā€ tab; there, check if there are any listeners attached that originate from the table library (it sounds as if it was either a ā€œchangeā€ or a ā€œblurā€ event). You can then try to trigger that event manually, for example:

$(document).on('click', '.clearBtn', function () {
  // Trigger a "change" event on the input after having cleared it
  $(this).hide().prev('input[type="search"]').val('').trigger('change')
})

But again itā€™s hard to tell without seeing the app in action.

Anyway glad I could help so far! :-)

1 Like

Oh you smashed it, well doneā€¦

thatā€™s it thank you m3g4p0p

pizza time!!!

1 Like

Lol, could you help me with one last one and iā€™ll leave you beā€¦

if there text in there and the user refreshes the page, can you show me how to get the clear cross to appear, then youā€™ll be more awsomerer

its this one

$(document).ready(function () {
        $("input[type='search']").each(function (index) {
            if ($(this).val().length != 0) {
                $(this).next(".clearBtn").show();
            }
        });
    });

Will be giving it a go myself too.

Going with what you gave me, Iā€™m thinking something along this line but not sure if load is the right call

    $(document).on('load', 'input[type="search"]', function () {
        var $next = $(this).next()

        // Create a clear button if it does not exist
        var $clearBtn = $next.is('.clearBtn')
            ? $next
            : $('<span class="clearBtn">x</span>').insertAfter(this)
    })

Well does the library provide any hooks (such as custom events or callbacks) to notify you when the view got updated? You could then dispatch ā€œkeyupā€ events on the search fields to trigger the check.

As for the load event delegation, that would only work for images and the like that have a src that gets loaded ā€“ elements themselves donā€™t trigger an event when they get attached to the DOM. You can however observe DOM modifications with a mutation observer (although hooks would probably be preferable performance-wise):

var observer = new MutationObserver(function (records) {
  records.forEach(function (record) {
    if (record.type === 'childList') {
      // Find all search fields among the added nodes, and 
      // dispatch a "keyup" event to trigger the check
      $(record.addedNodes)
        .find('input[type="search"]')
        .addBack('input[type="search"]')
        .trigger('keyup')
    }
  })  
})

// Observe the complete body for changes to the child 
// list, i.e. nodes that were added, moved or removed
observer.observe(document.body, {
  childList: true,
  subtree: true
})

(Although in that case you wouldnā€™t actually have to use event delegation ā€“ you could as well initialize the clear buttons as before whenever a new search field gets added to the DOM.)