138

Let's say I have an html form. Each input/select/textarea will have a corresponding <label> with the for attribute set to the id of it's companion. In this case, I know that each input will only have a single label.

Given an input element in javascript — via an onkeyup event, for example — what's the best way to find it's associated label?

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 6
    Note that an element may have more than one label, I've seen some applications (SAP for one) that have multiple labels per element. – Motti Jul 08 '09 at 06:28
  • 2
    `function getInputLabel(thisElement) { var theAssociatedLabel,elementID; elementID = thisElement.id; theAssociatedLabel = thisElement.parentNode.querySelector("label[for='" + elementID + "']"); console.log('theAssociatedLabel.htmlFor: ' + theAssociatedLabel.htmlFor); theAssociatedLabel.style.backgroundColor = "green";//Set the label background color to green };` – Alan Wells Oct 28 '16 at 03:02
  • or just node.parentNode.querySelector("label[for='" + node.id + "']").innerHTML; lol –  Nov 25 '19 at 04:42

23 Answers23

118

If you are using jQuery you can do something like this

$('label[for="foo"]').hide ();

If you aren't using jQuery you'll have to search for the label. Here is a function that takes the element as an argument and returns the associated label

function findLableForControl(el) {
   var idVal = el.id;
   labels = document.getElementsByTagName('label');
   for( var i = 0; i < labels.length; i++ ) {
      if (labels[i].htmlFor == idVal)
           return labels[i];
   }
}
Mouna Cheikhna
  • 38,870
  • 10
  • 48
  • 69
TonyB
  • 3,882
  • 2
  • 25
  • 22
104

First, scan the page for labels, and assign a reference to the label from the actual form element:

var labels = document.getElementsByTagName('LABEL');
for (var i = 0; i < labels.length; i++) {
    if (labels[i].htmlFor != '') {
         var elem = document.getElementById(labels[i].htmlFor);
         if (elem)
            elem.label = labels[i];         
    }
}

Then, you can simply go:

document.getElementById('MyFormElem').label.innerHTML = 'Look ma this works!';

No need for a lookup array :)

Dan Herbert
  • 99,428
  • 48
  • 189
  • 219
FlySwat
  • 172,459
  • 74
  • 246
  • 311
  • Can you explain where your use of an expando property differs from mine? I do not use any "lookup array", I use an object with properties. There is only syntactical difference between "element.label", "element['label']" or "lookup['elementId']". Behind the scenes they are *the same thing*. – Tomalak Nov 13 '08 at 09:28
  • 5
    This is nicer because I don't have to keep a separate object around- the relationship is stored with the input element directly. – Joel Coehoorn Nov 13 '08 at 14:07
  • 3
    This should be handled by the browser – andho Jan 25 '13 at 04:43
54

There is a labels property in the HTML5 standard which points to labels which are associated to an input element.

So you could use something like this (support for native labels property but with a fallback for retrieving labels in case the browser doesn't support it)...

var getLabelsForInputElement = function(element) {
    var labels = [];
    var id = element.id;

    if (element.labels) {
        return element.labels;
    }

    id && Array.prototype.push
        .apply(labels, document.querySelector("label[for='" + id + "']"));

    while (element = element.parentNode) {
        if (element.tagName.toLowerCase() == "label") {
            labels.push(element);
        }  
    }

    return labels;
};

// ES6
var getLabelsForInputElement = (element) => {
    let labels;
    let id = element.id;

    if (element.labels) {
        return element.labels;
    }

    if (id) {
        labels = Array.from(document.querySelector(`label[for='${id}']`)));
    }

    while (element = element.parentNode) {
        if (element.tagName.toLowerCase() == "label") {
            labels.push(element);
        }  
    }

    return labels;
};

Even easier if you're using jQuery...

var getLabelsForInputElement = function(element) {
    var labels = $();
    var id = element.id;

    if (element.labels) {
        return element.labels;
    }

    id && (labels = $("label[for='" + id  + "']")));

    labels = labels.add($(element).parents("label"));

    return labels;
};
Antti29
  • 2,953
  • 12
  • 34
  • 36
alex
  • 479,566
  • 201
  • 878
  • 984
32

document.querySelector("label[for=" + vHtmlInputElement.id + "]");

This answers the question in the simplest and leanest manner. This uses vanilla javascript and works on all main-stream proper browsers.

Luigi D'Amico
  • 665
  • 7
  • 11
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/16769830) – loki Jul 20 '17 at 08:38
  • 5
    This most definitely provides an answer to the question @loki Just because there is no further explanation to it, that doesn't mean it is wrong or didn't attempt to answer it. – geisterfurz007 Jan 30 '18 at 14:03
  • it works for me, but you should wrap `vHtmlInputElement.id into ''` like this: `document.querySelector("label[for='" + vHtmlInputElement.id + "']");` – Denis Davydov May 14 '23 at 07:50
  • This needs to be escaped properly. – Flimm Jul 07 '23 at 22:21
24

I am a bit surprised that nobody seems to know that you're perfectly allowed to do:

<label>Put your stuff here: <input value="Stuff"></label>

Which won't get picked up by any of the suggested answers, but will label the input correctly.

Here's some code that does take this case into account:

$.fn.getLabels = function() {
    return this.map(function() {
        var labels = $(this).parents('label');
        if (this.id) {
            labels.add('label[for="' + this.id + '"]');
        }
        return labels.get();
    });
};

Usage:

$('#myfancyinput').getLabels();

Some notes:

  • The code was written for clarity, not for performance. More performant alternatives may be available.
  • This code supports getting the labels of multiple items in one go. If that's not what you want, adapt as necessary.
  • This still doesn't take care of things like aria-labelledby if you were to use that (left as an exercise to the reader).
  • Using multiple labels is a tricky business when it comes to support in different user agents and assistive technologies, so test well and use at your own risk, etc. etc.
  • Yes, you could also implement this without using jQuery. :-)
Gijs
  • 5,201
  • 1
  • 27
  • 42
  • 7
    Nice to see someone bringing up implicit label association as opposed to making an assumption there will always be a `for` attribute on the `label` element. – vhs Mar 25 '14 at 01:47
15

Earlier...

var labels = document.getElementsByTagName("LABEL"),
    lookup = {},
    i, label;

for (i = 0; i < labels.length; i++) {
    label = labels[i];
    if (document.getElementById(label.htmlFor)) {
        lookup[label.htmlFor] = label;
    }
}

Later...

var myLabel = lookup[myInput.id];

Snarky comment: Yes, you can also do it with JQuery. :-)

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    Nice. I'll refactor that just a little so that it builds the lookup the first time I call the method, but it should do the trick. – Joel Coehoorn Nov 12 '08 at 22:13
  • I implied that you would build the lookup only once. – Tomalak Nov 12 '08 at 22:15
  • Be aware that there was a small error in it: Use the "htmlFor" property, not "for", as the latter is a reserved word in JS. I tend to forget that when using label objects... :-) – Tomalak Nov 12 '08 at 22:18
  • I've got a way to do this without a lookup array above. – FlySwat Nov 12 '08 at 22:25
  • I meant move the init code inside the method, not do it just once :( – Joel Coehoorn Nov 13 '08 at 02:48
  • You did not mention any "method", and currently I'm not even sure if I know what you refer to as "init code". All I was saying is that for one page the look-up object needs to be built only once. *Where* and *when* you do it is entirely up to you. – Tomalak Nov 13 '08 at 09:21
  • I thought my use of the words "earlier" and "later" would have made it clear that the former would happen on document load, and the latter would happen anytime you need it. – Tomalak Nov 13 '08 at 09:32
  • I think there is a typo on line 3 : ";" – bogatyrjov Aug 19 '13 at 13:53
  • And "labels[i]" instead of "label" on line 5 – bogatyrjov Aug 19 '13 at 14:01
  • @Jevgeni Oops that went unnoticed for quite some time. Thanks for suggesting a fix. I've updated the entire code to match recent JS style & added an "if label target exists" check, too. – Tomalak Aug 19 '13 at 14:13
14

All the other answers are extremely outdated!!

All you have to do is:

input.labels

HTML5 has been supported by all of the major browsers for many years already. There is absolutely no reason that you should have to make this from scratch on your own or polyfill it! Literally just use input.labels and it solves all of your problems.

davidnagli
  • 659
  • 1
  • 6
  • 12
  • 1
    According to [this page](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/labels#Browser_compatibility), `input.labels` isn't supported by IE or Edge, and Firefox only just added the support. – Antti29 Feb 06 '18 at 09:42
  • @Antti29 You can add the functionality using a shim: https://github.com/termi/ES5-DOM-SHIM You can see the feature being added here: https://github.com/termi/ES5-DOM-SHIM/blob/bd9c253cbace62d9ac259cf714bfd1dbfaadcfae/__SRC/a.js#L3935 – ADJenks Apr 23 '20 at 20:05
  • 2
    This should be the answer in 2022. – Joshua Dance Sep 27 '22 at 21:51
  • This should be the accepted answer. Internet Explorer (IE)? lol ... IE is replaced by EDGE. You can use it in every browser: https://caniuse.com/?search=labels – Kevin O. May 24 '23 at 16:31
8

with jquery you could do something like

var nameOfLabel = someInput.attr('id');
var label = $("label[for='" + nameOfLabel + "']");
Michael
  • 63
  • 4
AndreasKnudsen
  • 3,453
  • 5
  • 28
  • 33
4

If you're willing to use querySelector (and you can, even down to IE9 and sometimes IE8!), another method becomes viable.

If your form field has an ID, and you use the label's for attribute, this becomes pretty simple in modern JavaScript:

var form = document.querySelector('.sample-form');
var formFields = form.querySelectorAll('.form-field');

[].forEach.call(formFields, function (formField) {
    var inputId = formField.id;
    var label = form.querySelector('label[for=' + inputId + ']');
    console.log(label.textContent);
});

Some have noted about multiple labels; if they all use the same value for the for attribute, just use querySelectorAll instead of querySelector and loop through to get everything you need.

Brendan
  • 868
  • 1
  • 16
  • 38
3

A really concise solution using ES6 features like destructuring and implicit returns to turn it into a handy one liner would be:

const getLabels = ({ labels, id }) => labels || document.querySelectorAll(`label[for=${id}]`)

Or to simply get one label, not a NodeList:

const getFirstLabel = ({ labels, id }) => labels && labels[0] || document.querySelector(`label[for=${id}]`)
yaakov
  • 4,568
  • 4
  • 27
  • 51
2

Answer from Gijs was most valuable for me, but unfortunately the extension does not work.

Here's a rewritten extension that works, it may help someone:

jQuery.fn.getLabels = function () {
    return this.map(function () {
        var parentLabels = $(this).parents('label').get();
        var associatedLabels = this.id ? associatedLabels = $("label[for='" + this.id + "']").get() : [];
        return parentLabels.concat(associatedLabels);
    });
};
OzrenTkalcecKrznaric
  • 5,535
  • 4
  • 34
  • 57
2

Solution One <label>: One <input>

Using HTML 5.2 reference Considering the <label> pointing to <input> using for=, the labels element will be a non empty array, and act as a link to the <label> element, accessing all properties of it, including its id=.

function myFunction() {
  document.getElementById("p1").innerHTML = "The first label associated with input: <b>" + document.getElementById("input4").labels[0].id + "</b>";

}
<form>
  <label id="theLabel" for="input4">my id is "theLabel"</label>
  <input name="name1" id="input4" value="my id is input4">
  <br>
</form>

<p>Click the "click me" button to see the label properties</p>

<button onclick="myFunction()">click me</button>


<p id="p1"></p>

Solution Many <label>: One <input>

With more than one <label> using for=, you can make a loop to show all of them, like this:

function myFunction2() {

var x = document.getElementById("input7").labels;
let text = "";
for (let i = 0; i < x.length; i++) {
  text += x[i].id + "<br>";
}
document.getElementById("p7").innerHTML = text;
}
<b>Three labels for one input</b><br>
<br>
<form>
  <label id="theLabel2" for="input7">my id is "theLabel2</label><br>
  <label id="theLabel3" for="input7">my id is "theLabel3</label><br>
  <label id="theLabel4" for="input7">my id is "theLabel4</label><br>
  <input name="name1" id="input7" value="my id is input7">
  <br>
</form>

<p>Click the "click me" button to see the label properties</p>
<button onclick="myFunction2()">click me2</button>

<p id="p7"></p>
2
$("label[for='inputId']").text()

This helped me to get the label of an input element using its ID.

haifacarina
  • 1,212
  • 1
  • 12
  • 18
1

It is actually far easier to add an id to the label in the form itself, for example:

<label for="firstName" id="firstNameLabel">FirstName:</label>

<input type="text" id="firstName" name="firstName" class="input_Field" 
       pattern="^[a-zA-Z\s\-]{2,25}$" maxlength="25"
       title="Alphabetic, Space, Dash Only, 2-25 Characters Long" 
       autocomplete="on" required
/>

Then, you can simply use something like this:

if (myvariableforpagelang == 'es') {
   // set field label to spanish
   document.getElementById("firstNameLabel").innerHTML = "Primer Nombre:";
   // set field tooltip (title to spanish
   document.getElementById("firstName").title = "Alfabética, espacio, guión Sólo, 2-25 caracteres de longitud";
}

The javascript does have to be in a body onload function to work.

Just a thought, works beautifully for me.

1

As it has been already mentionned, the (currently) top-rated answer does not take into account the possibility to embed an input inside a label.

Since nobody has posted a JQuery-free answer, here is mine :

var labels = form.getElementsByTagName ('label');
var input_label = {};
for (var i = 0 ; i != labels.length ; i++)
{
    var label = labels[i];
    var input = label.htmlFor
              ? document.getElementById(label.htmlFor)
              : label.getElementsByTagName('input')[0];
    input_label[input.outerHTML] = 
        (label.innerText || label.textContent); // innerText for IE8-
}

In this example, for the sake of simplicity, the lookup table is directly indexed by the input HTML elements. This is hardly efficient and you can adapt it however you like.

You can use a form as base element, or the whole document if you want to get labels for multiple forms at once.

No checks are made for incorrect HTML (multiple or missing inputs inside labels, missing input with corresponding htmlFor id, etc), but feel free to add them.

You might want to trim the label texts, since trailing spaces are often present when the input is embedded in the label.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
1

The best answer works perfectly fine but in most cases, it is overkill and inefficient to loop through all the label elements.

Here is an efficent function to get the label that goes with the input element:

function getLabelForInput(id)
{
    var el = document.getElementById(id);
    if (!el)
        return null;
    var elPrev = el.previousElementSibling;
    var elNext = el.nextElementSibling;
    while (elPrev || elNext)
    {
        if (elPrev)
        {
            if (elPrev.htmlFor === id)
                return elPrev;
            elPrev = elPrev.previousElementSibling;
        }
        if (elNext)
        {
            if (elNext.htmlFor === id)
                return elNext;
            elNext = elNext.nextElementSibling;
        }
    }
    return null;
}

For me, this one line of code was sufficient:

el = document.getElementById(id).previousElementSibling;

In most cases, the label will be very close or next to the input, which means the loop in the above function only needs to iterate a very small number of times.

Dan Bray
  • 7,242
  • 3
  • 52
  • 70
0

I know this is old, but I had trouble with some solutions and pieced this together. I have tested this on Windows (Chrome, Firefox and MSIE) and OS X (Chrome and Safari) and believe this is the simplest solution. It works with these three style of attaching a label.

<label><input type="checkbox" class="c123" id="cb1" name="item1">item1</label>

<input type="checkbox" class="c123" id="cb2" name="item2">item2</input>

<input type="checkbox" class="c123" id="cb3" name="item3"><label for="cb3">item3</label>

Using jQuery:

$(".c123").click(function() {
    $cb = $(this);
    $lb = $(this).parent();
    alert( $cb.attr('id') + ' = ' + $lb.text() );
});

My JSFiddle: http://jsfiddle.net/pnosko/6PQCw/

Peter Nosko
  • 1,078
  • 1
  • 9
  • 10
  • This isn't actually working -- it's just returning the text of the parent element, which sometimes happens to be the text you want. – John Hascall Jul 01 '16 at 05:40
0

I have made for my own need, can be useful for somebody: JSFIDDLE

$("input").each(function () {
    if ($.trim($(this).prev('label').text()) != "") {
        console.log("\nprev>children:");
        console.log($.trim($(this).prev('label').text()));
    } else {
        if ($.trim($(this).parent('label').text()) != "") {
            console.log("\nparent>children:");
            console.log($.trim($(this).parent('label').text()));
        } else {
            if ($.trim($(this).parent().prev('label').text()) != "") {
                console.log("\nparent>prev>children:");
                console.log($.trim($(this).parent().prev('label').text()));
            } else {
                console.log("NOTFOUND! So set your own condition now");
            }
        }
    }
});
itsazzad
  • 6,868
  • 7
  • 69
  • 89
0

I am bit surprised no one is suggesting to use the CSS relationship method?

in a style sheet you can reference a label from the element selector:

<style>

//for input element with class 'YYY'
input.YYY + label {}

</style>

if the checkbox has an id of 'XXX' then the label would be found through jQuery by:

$('#XXX + label');

You can also apply .find('+ label') to return the label from a jQuery checkbox element, ie useful when looping:

$('input[type=checkbox]').each( function(){
   $(this).find('+ label');
});
Gordon Rouse
  • 301
  • 3
  • 6
0

Use a JQuery selector:

$("label[for="+inputElement.id+"]")
Mike McKay
  • 2,388
  • 1
  • 29
  • 32
0

For future searchers... The following is a jQuery-ified version of FlySwat's accepted answer:

var labels = $("label");
for (var i = 0; i < labels.length; i++) {
    var fieldId = labels[i].htmlFor;
    if (fieldId != "") {
        var elem = $("#" + fieldId);
        if (elem.length != 0) {
            elem.data("label", $(labels[i]));   
        }
    }
}

Using:

$("#myFormElemId").data("label").css("border","3px solid red");
ObjectType
  • 1,159
  • 10
  • 15
  • +1, but from what I see there's not reason to use the jQuery version for the first block: it's not any shorter or more readable, and the plain javascript will likely perform better. It is nice to have a jQuery example around of how to use it once created, though. – Joel Coehoorn Aug 19 '11 at 13:12
  • Obviously to many of us there isn't much of a technical reason for this approach, rather a reason, at least in my case, is an HR one. Imagine you have a team that included designers, integrators, and even Jr devs who you have slowing got used to using jQuery. Keeping things in the jQuery paradigm can help keep productivity up and reduce frustration. – ObjectType Aug 19 '11 at 15:53
  • This isn't very *jQuery-ified*. Why the `for` loop over `each()`? Why the `htmlFor` instead of `attr("for")`? – alex Sep 05 '13 at 10:24
  • Depends on perspective, I guess. The consumption was jQuery-ified, not the internals. – ObjectType Sep 06 '13 at 11:10
0

If you use the for attribute, you can use querySelector(...) to get
the associated label.

HTML/JavaScript

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <label for="myCheckbox">Log Report to Console?</label>
    <input name="myCheckbox" type="checkbox" oninput="doSomething(event)" />

    <script type="text/javascript">
        function doSomething(e) {
            const query = `label[for="${e.target.name}"]`; // This is string interpolation NOT JQuery
            const label = document.querySelector(query);
        }
    </script>
</body>
</html>

Plain JavaScript

function doSomething(e) {

    // const query = `label[for="${e.target.name}"]`; // This is string interpolation NOT JQuery
    // Maybe it is safer to use ".getAttribute"
    const query = `label[for="${e.target.getAttribute("name")}"]`;
    const label = document.querySelector(query);

    // Do what you want with the label here...
    debugger; // You're welcome
    console.log(label);
}
Mark Davich
  • 512
  • 1
  • 5
  • 16
-1

jQueryUI has a labels() function:

$input.labels(), basically does what $('label[for="name"]') does.

https://api.jqueryui.com/labels/

goulashsoup
  • 2,639
  • 2
  • 34
  • 60