0

I have been using some code to add divs to my buttons for styling using the code below, it works fine on the first instance of the button but it does not add the divs to any buttons after that? what am I missing? I'm willing to learn and have tried to Google this but I'm getting buried deep in things I don't fully understand just yet. Would be great if any answer could be in plain Javascript and not jQuery.

JS

// Parent Element
const el = document.querySelector(".myclass");

// Create New Element
const newEl = document.createElement("span");
newEl.classList= "cm-bg";

const newEl2 = document.createElement("span");
newEl2.classList= "cm-base";

// Insert New Element BEFORE an Element
el.before(newEl);
el.before(newEl2);

HTML

<div class="elementor-button-wrapper">
   <a href="#" class="elementor-button-link" role="button">
      <span class="elementor-button-content-wrapper">
         <span class="elementor-button-text">Click here</span>
      </span>
   </a>
</div>
Daweed
  • 1,419
  • 1
  • 9
  • 24
Matu
  • 17
  • 9

1 Answers1

2

querySelector finds the first matching element in the document. So your code always adds elements to the first .myclass in your document.

If you want to find all matching elements and update them, you use querySelectorAll and loop through the results:

const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
    // Create New Element
    const newEl = document.createElement("span");
    newEl.className = "cm-bg";      // *** See comment on question
    const newEl2 = document.createElement("span");
    newEl2.className = "cm-base";   // *** See comment on question

    // Insert New Element AFTER an Element
    el.before(newEl);
    el.before(newEl2);
}

Live Example:

const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
    // Create New Element
    const newEl = document.createElement("span");
    newEl.className = "cm-bg";      // *** See comment on question
    const newEl2 = document.createElement("span");
    newEl2.className = "cm-base";   // *** See comment on question

    // Insert New Element AFTER an Element
    el.before(newEl);
    el.before(newEl2);
}
.cm-bg::after {
    content: "cm-bg"
}
.cm-base::after {
    content: "cm-base"
}
<div class="elementor-button-wrapper">
    <a href="#" class="elementor-button-link" role="button">
        <span class="elementor-button-content-wrapper">
            <span class="elementor-button-text">Click here</span>
        </span>
    </a>
</div>
<div class="elementor-button-wrapper">
    <a href="#" class="elementor-button-link" role="button">
        <span class="elementor-button-content-wrapper">
            <span class="elementor-button-text">Click here</span>
        </span>
    </a>
</div>
<div class="elementor-button-wrapper">
    <a href="#" class="elementor-button-link" role="button">
        <span class="elementor-button-content-wrapper">
            <span class="elementor-button-text">Click here</span>
        </span>
    </a>
</div>
<div class="elementor-button-wrapper">
    <a href="#" class="elementor-button-link" role="button">
        <span class="elementor-button-content-wrapper">
            <span class="elementor-button-text">Click here</span>
        </span>
    </a>
</div>

That relies on the NodeList from querySelectorAll being iterable, which it is in modern environments (and per specification). If you need to handle older environments, see my answer here for how to polyfill it. Or just use a for loop:

for (let i = 0; i < list.length; ++i) {
    const el = list[i];
    // ...rest of loop body here...
}

Side note: Beware that no version of IE supports the before method on ChildNode. IE is actively being discontinued by Microsoft, but still has significant presence in large corporate or government installations.

FWIW, you can use insertAdjacentHTML which is universally supported and lets you write the elements as HTML (if that's desireable):

const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
    el.insertAdjacentHTML(
        "beforestart",
        "<span class=cm-bg></span><span class=cm-base></span>"
    );
}

Or just use insertBefore:

const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
    // Create New Element
    const newEl = document.createElement("span");
    newEl.className = "cm-bg";      // *** See comment on question
    const newEl2 = document.createElement("span");
    newEl2.className = "cm-base";   // *** See comment on question

    // Insert New Element AFTER an Element
    el.parentElement.insertBefore(newEl, el);
    el.parentElement.insertBefore(newEl2, el);
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I have added this and it is still only affecting my frist instance of the button – Matu Feb 06 '21 at 12:55
  • @Matu - I'm afraid you must have not quite applied it correctly. I'd do a live example, but there is no element with the class `myclass` in your HTML, so I have no idea which element you want to add these `span`s near. – T.J. Crowder Feb 06 '21 at 12:57
  • I'm testing it here https://codepen.io/matthew-reilly/pen/NWbNxoY – Matu Feb 06 '21 at 12:58
  • I updated the original post also to include the class – Matu Feb 06 '21 at 13:08
  • @Matu - I've been assuming that this code runs in response to some event. Are you trying to add to **multiple** buttons in this code, all at once? – T.J. Crowder Feb 06 '21 at 13:41
  • As in the Codepen, there will be more than one button on the page at any time and I want the two new spans added to the CSS class .elementor-button-text for each one. – Matu Feb 06 '21 at 13:49
  • @Matu - The way SO works, your whole question (including any necessary code) has to be **in** your question, not just linked. Three reasons: People shouldn't have to go off-site to help you; some sites are blocked for some users; and links rot, making the question and its answers useless to people in the future. – T.J. Crowder Feb 06 '21 at 14:46
  • @Matu - I've updated to show handling a list of elements. – T.J. Crowder Feb 06 '21 at 14:50
  • Thanks, I will take that on board with the whole code etc, I got the code working but now the text in the button is being drawn over by the hover background effect, I have tried .after and higher z-index on .elementor-button-text but it is still below... any ideas? actually I just tried on my site and it is fine, it is Codepen that was having the issue. thanks for your help – Matu Feb 06 '21 at 15:13