Better way to achieve this vanilla javascript DOM traversal?

192 views Asked by At

I have some auto-generated forms from Marketo, meaning I am forced to work around what is provided. When there is a checkbox with only one value, I need to add a class to the parent label (There is a comment that indicates which one).

The checkbox in question could be any field, not just specifically "explicitOptin", so I can't select based off that (at least not initially). I also have to make sure I don't accidentally select checkboxes with two or more values.

Here is the javascript I came up with:

var ckBox = document.querySelector('.mktoCheckboxList .mktoField:only-of-type').parentNode.previousSibling.previousSibling;
if (ckBox.className.indexOf('mktoSingleCheckbox') == -1) {
    ckBox.className += ' mktoSingleCheckbox';
}
<form id="mktoForm_1690" novalidate="novalidate" class="mktoForm mkt*emphasized text*oHasWidth mktoLayoutLeft" data-styles-ready="true">
<div class="mktoFormRow">
    <div class="mktoFormRow">
        <div class="mktoFieldDescriptor mktoFormCol">
            <div class="mktoOffset"></div>
            <div class="mktoFieldWrap">
                <label for="hazmatchooseallthatapply" class="mktoLabel mktoHasWidth">
                    <div class="mktoAsterix">*</div>Hazmat (choose all that apply):</label>
                <div class="mktoGutter mktoHasWidth"></div>
                <div class="mktoLogicalField mktoCheckboxList mktoHasWidth">
                    <input name="hazmatchooseallthatapply" id="mktoCheckbox_9480_0" type="checkbox" value="Hazardous Materials" class="mktoField">
                    <label for="mktoCheckbox_9480_0">Hazardous Materials</label>
                    <input name="hazmatchooseallthatapply" id="mktoCheckbox_9480_1" type="checkbox" value="Hazardous Waste" class="mktoField">
                    <label for="mktoCheckbox_9480_1">Hazardous Waste</label>
                    <input name="hazmatchooseallthatapply" id="mktoCheckbox_9480_2" type="checkbox" value="Fuel Tanker" class="mktoField">
                    <label for="mktoCheckbox_9480_2">Fuel Tanker</label>
                </div>
                <div class="mktoClear"></div>
            </div>
            <div class="mktoClear"></div>
        </div>
        <div class="mktoClear"></div>
    </div>
    <div class="mktoClear"></div>
</div>
<div class="mktoFormRow">
    <div class="mktoFieldDescriptor mktoFormCol">
        <div class="mktoOffset"></div>
        <div class="mktoFieldWrap">
            <!--This label directly below is the element that needs the new class-->
            <label for="explicitOptin" class="mktoLabel mktoHasWidth mktoSingleCheckbox">
                <div class="mktoAsterix">*</div>Yes, please sign me up to receive information from PrePass by email.</label>
            <div class="mktoGutter mktoHasWidth"></div>
            <div class="mktoLogicalField mktoCheckboxList mktoHasWidth mktoValid">
                <!-- This is the single checkbox input that I am selecting with my javascript -->
                <input name="explicitOptin" id="explicitOptin" type="checkbox" value="yes" class="mktoField">
                <label for="explicitOptin"></label>
            </div>
            <div class="mktoClear"></div>
        </div>
        <div class="mktoClear"></div>
    </div>
    <div class="mktoClear"></div>
</div>

I feel like there has to be a better way than just stringing together parentNode and previousSibling methods.

Any suggestions? I was thinking a possible alternative would be to use the CSS selector that I'm using, but then grab the ID value and search elements that have a for value of that ID. Maybe just stringing together parentNode and previousSiblings is the way to go...?

1

There are 1 answers

0
ContinuousLoad On BEST ANSWER

As of writing this, your solution seems to be the best route to go, with the exception of using element.classList instead of element.className.

Optional reformating leads to this (long) one-liner:

document.querySelector('.mktoCheckboxList .mktoField:only-of-type')
  .parentNode.previousSibling.previousSibling
  .classList.add('mktoSingleCheckbox');

LOOKING TO THE FUTURE, browsers will let us use the HTMLInputElement.labels API to gather labels associated with an <input>.

#                                                                   WOW!
document.querySelector('.mktoCheckboxList .mktoField:only-of-type').labels[0]
  .classList.add('mktoSingleCheckbox');

Unfortunately, browser support for this API is very limited right now, and trying to get associated labels manually is perhaps more cumbersome than your current method.

Hope this helps!