How to select the first child and the last child of a group of elements with JavaScript?

Hello,
The following code didn’t work.

document.querySelectorAll(".x").forEach( (element)=>{
    element.firstChild.style.marginBottom = "0";
    element.lastChild.style.marginTop = "0";
});

The error I got is:

Uncaught TypeError: Cannot set properties of undefined (setting ‘marginBottom’)

Why is the selection undefined?

I can change these styles with CSS but primarily for the sake of learning I ask about JavaScript.

Hi,

The problem is likely that element.firstChild is a newline.

Try using firstElementChild and lastElementChild instead.

document.querySelectorAll('.x').forEach((element)=>{
  element.firstElementChild.style.marginBottom = '0';
  element.lastElementChild.style.marginTop = '0';
});

There might be a bias because I use a content management system but isn’t that changing what’s inside a box and not the box itself?

Thanks,

I don’t understand. Sorry.

Did you try the code and did it work?

1 Like

I tried it but it didn’t get the expected outcome.

Please never mind though, the problem probably comes from the content management system with all its JavaScript-induced-dynamicity and elements with CSS display: none in the same wrapper on which its children I work…

Oh ok. Well good luck with that :slight_smile:

1 Like

Opp, sorry,
It does work as expected !
I just checked the last element and indeed its CSS margin-top property and value have strike-through on them:

x


But, a strange problem is the reduction of some margin inside the element itself (between two children of the element itself, as evident below in Hebrew and English).

Before:

2

After:

1

Just to add to @James_Hibbard’s comment about the newline. firstChild will pick up on whitespace.

To illustrate
html

<ul id='list-01'>
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
</ul>

<ul id='list-02'><li>item 1</li><li>item 2</li><li>item 3</li></ul>

javascript

const list01 = document.querySelector('ul#list-01')
const list02 = document.querySelector('ul#list-02')

console.log(list01.firstChild) // #text -> ' \n'
console.log(list02.firstChild) // li -> <li>item 1</li>

console.log(list01.firstChild.nodeType) // 3 TEXT_NODE
console.log(list02.firstChild.nodeType) // 1 ELEMENT_NODE

It’s why before being able to use code like firstElementChild or nextElementSibling under the hood Jquery would use code like this

// nextSibling
function next( elem ) {
    do {
        elem = elem.nextSibling;
    } while ( elem && elem.nodeType != 1 ); // while not an element
    return elem;
}

// firstChild
function first( elem ) {
    elem = elem.firstChild;
    return (elem && elem.nodeType != 1) // if not an element
        ? next ( elem ) 
        : elem;
}
2 Likes

I may be missing the obvious but couldn’t you just do this?

document.querySelector(".x:first-child").style.marginBottom = "0";
document.querySelector(".x:last-child").style.marginTop = "0";

Assuming that .x is going to be either the first or last elements in that group. If other stuff is mixed in with those elements then it would not work.

Also bearing in mind that due to collapsing margins the above will have no effect if siblings have their own top and bottom margins.

1 Like

I assume I could :slight_smile: for now I got the desired effect directly by CSS but it’s good to know it, thanks a lot.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.