avatar

DOM Manipulation Practice

Exercises One

📺JavaScript DOM Exercises 01
🔗Codepen

  • 高亮文本关键词⭐
1
2
3
4
5
6
/* 假设选择长度大于8的词语作为关键词 */
const p = document.querySelector('p');
p.innerHTML = p.innerText
.split(' ')
.map(word => word.length > 8 ? `<span style="background-color: yellow">${word}</span>` : word)
.join(' ');

  • 为页面添加一个链接节点

    1
    2
    3
    4
    const link = document.createElement("a");
    link.href = "https://forcemipsum.com/";
    link.innerText = "This is a new link";
    document.body.appendChild(link);

  • 将文本段落按句子分割成段⭐

    1
    2
    3
    4
    const p = document.querySelector('p');
    p.innerHTML = p.innerText
    .split('. ')
    .join('.</p><p>') + '</p>';

  • 计算文本的单词数并标注在标题下方

    1
    2
    3
    4
    5
    6
    7
    8
    const p = document.querySelector('p');
    const len = p.innerText
    .split(' ')
    .length;

    const wordsCount = document.createElement('div');
    wordsCount.innerText = `${len} words`;
    document.body.insertBefore(wordsCount, p);

  • 文本指定内容替换

    1
    2
    3
    4
    5
    /* 假设将文本中的问号和感叹号分别替换为不同的emoji */
    const p = document.querySelector('p');
    p.innerHTML = p.innerHTML
    .replace(/\?/g, '🤔')
    .replace(/\!/g, '😲');


Exercises Two

📺JavaScript DOM Exercises 02
🔗Codepen

  • 在输入框前插入标签

    1
    2
    3
    document.getElementById('username').insertAdjacentHTML('beforebegin', '<label for="username">Username:</label>');
    document.getElementById('password').insertAdjacentHTML('beforebegin', '<label for="password">Password:</label>');
    document.getElementById('confirmPassword').insertAdjacentHTML('beforebegin', '<label for="confirmPassword">Confirm Password:</label>');

  • 在输入框后添加验证提示信息⭐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const checkInput = (event => {
    const warning = Array.from(event.target.parentNode.querySelectorAll('span'));
    if (event.target.value === '' && !warning.length) {
    event.target.insertAdjacentHTML('afterend', '<span style="color:red">Please enter your information!</span>');
    }
    if (event.target.value !== '' && warning.length) {
    warning.forEach(element => {element.remove()});
    }
    });
    document.getElementById('username').addEventListener('blur', checkInput);
    document.getElementById('password').addEventListener('blur', checkInput);
    document.getElementById('confirmPassword').addEventListener('blur', checkInput);

  • 验证“密码”与“确认密码”的一致性

    1
    2
    3
    4
    5
    6
    /* 可能不是很正确,这里使用了 event.target.value !== '' 来避免和上一条提示信息同时出现 */
    document.getElementById('confirmPassword').addEventListener('blur', event => {
    if (event.target.value !== document.getElementById('password').value && event.target.value !== '') {
    event.target.insertAdjacentHTML('afterend', '<span style="color:red">Passwords should match!</span>');
    }
    });

  • 仅当所有必填项都填写后按钮才可点击

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* 不太正确,按钮的可点击性没有密码一致性的限制 */
    const button = document.querySelector('button');
    button.setAttribute('disabled', 'disabled');
    document.getElementById('registrationForm').addEventListener('change', (event) => {
    const filled = Array.from(document.querySelectorAll('input')).every(input => input.value);
    if (filled) {
    button.removeAttribute('disabled');
    }
    else {
    button.setAttribute('disabled', 'disabled');
    }
    });

  • 修改/添加提交表单触发事件

    1
    2
    3
    4
    5
    6
    /* 原先我认为直接在按钮上添加监听事件即可的,但是表单会默认提交,这有时候并不是我们想要的,所以不如修改表单默认监听事件 */
    const form = document.getElementById('registrationForm');
    form.addEventListener('submit', (event) => {
    event.preventDefault();
    alert("OK");
    });


Exercises Three

📺JavaScript DOM Exercises 03
🔗Codepen

  • 添加条目

    1
    2
    3
    4
    5
    6
    7
    // 方法一:
    const newItem = document.createElement('li');
    newItem.innerText = '24/7 Phone support';
    document.querySelector('#pro-plan ul').appendChild(newItem);

    // 方法二:
    document.querySelector('#pro-plan ul').insertAdjacentHTML('beforeEnd', '<li>24/7 Phone support</li>');

  • 交换节点位置/布局⭐

    1
    2
    3
    4
    5
    6
    7
    // 方法一:交换DOM节点,将导致重排
    const basic = document.querySelector('#basic-plan');
    const pro = document.querySelector('#pro-plan');
    basic.parentNode.insertBefore(basic, pro);

    // 方法二:反转布局,将导致重绘
    document.querySelector('.card-deck').style['flex-direction'] = 'row-reverse';

  • 获取并重新计算页面中的数据

    1
    2
    3
    4
    const basicStorage = document.querySelector('#basic-plan .storage-amount');
    const proStorage = document.querySelector('#pro-plan .storage-amount');
    basicStorage.innerText = basicStorage.innerText * 1.5;
    proStorage.innerText = proStorage.innerText * 1.25;

  • 设置单选按钮组,根据选项显示不同信息⭐⭐⭐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    document.querySelector('.container').insertAdjacentHTML('afterBegin', `<label>
    Monthly
    <input type="radio" value="monthly" name="pricing" id="monthlyPricing" checked>
    </label>
    <label>
    Annual
    <input type="radio" value="annual" name="pricing" id="annualPricing">
    </label>`);

    const pricing = {
    monthly: {
    basic: '10 / month',
    pro: '30 / month'
    },
    annual: {
    basic: '100 / year',
    pro: '300 / year'
    }
    };

    const changePricing = (event) => {
    const selection = event.target.value;
    // 根据属性选择即可,不需要条件语句判断
    document.querySelector('#basic-plan .pricing').innerText = pricing[selection].basic;
    document.querySelector('#pro-plan .pricing').innerText = pricing[selection].pro;
    /*
    const basicPricing = document.querySelector('#basic-plan .pricing');
    const proPricing = document.querySelector('#pro-plan .pricing');
    if (selection === 'monthly') {
    basicPricing.innerText = pricing.monthly.basic;
    proPricing.innerText = pricing.monthly.pro;
    }
    else {
    basicPricing.innerText = pricing.annual.basic;
    proPricing.innerText = pricing.annual.pro;
    }
    */
    };
    Array.from(document.querySelectorAll('input[type="radio"]'))
    .forEach(radio => radio.addEventListener('change', changePricing));


Exercises Four

📺JavaScript DOM Exercises 04
🔗Codepen

  • 合并段落

    1
    2
    3
    const paragraphs = document.querySelectorAll('#hero p');
    paragraphs[0].insertAdjacentHTML('beforeEnd', paragraphs[1].innerText);
    paragraphs[1].remove();

  • 修改字体大小(需获取样式计算结果)⭐

    1
    2
    3
    4
    const menuItem = document.querySelector('.menu ul li');
    const menuFontSize = parseInt(window.getComputedStyle(menuItem).getPropertyValue('font-size'));
    Array.from(document.querySelectorAll('p')).forEach(p => p.style.fontSize = `${menuFontSize / 2}px`);
    //document.querySelector('p').style['font-size'] = `${menuFontSize / 2}px`;

  • 修改导航栏条目顺序

    1
    2
    3
    4
    // 注意,如果使用 childNodes 的话需要排除 "#text" 节点
    const aboutNode = document.querySelector('.menu ul li:nth-child(2)');
    const servicesNode = document.querySelector('.menu ul li:nth-child(3)');
    aboutNode.insertAdjacentElement('beforeBegin', servicesNode);

  • 添加鼠标悬停效果⭐⭐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const hoverEffect = (event) => {
    const element = event.target;
    element.style['background-color'] = '#f2f2f2';
    element.style['font-size'] = '20px';
    };
    const normalEffect = (event) => {
    const element = event.target;
    element.style['background-color'] = '#fff';
    element.style['font-size'] = '16px';
    };
    Array.from(document.querySelectorAll('.menu ul li')).forEach(item => {
    item.addEventListener('mouseover', hoverEffect);
    item.addEventListener('mouseleave', normalEffect);
    });


Exercises Five

📺JavaScript DOM Exercises 05
🔗Codepen

  • 复制元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const card = document.querySelector('.jobs .job-card');
    const card1 = card.cloneNode(true);
    const card2 = card.cloneNode(true);
    const card3 = card.cloneNode(true);
    /* 注意,这里需逆序插入 */
    card.insertAdjacentElement('afterEnd', card3);
    card.insertAdjacentElement('afterEnd', card1);
    card.insertAdjacentElement('afterEnd', card2);
    // const board = document.querySelector('.jobs');
    // board.appendChild(card1);
    // board.appendChild(card2);
    // board.appendChild(card3);

  • 修改指定DOM节点的子元素

    1
    2
    card1.querySelector('h3').innerText = "JavaScript Developer";
    // card1.childNodes[1].innerText = "JavaScript Developer";

  • 根据搜索框输入过滤节点⭐⭐⭐⭐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    document.querySelector('#search').addEventListener('keyup', (event) => {
    const element = event.target;
    const input = element.value;
    const cards = Array.from(document.querySelectorAll('.job-card'));
    cards.forEach((card) => {
    const title = card.querySelector('h3').innerText;
    // if (title == input) {
    if (title.toLowerCase().includes(input.toLowerCase())) {
    card.style.display = 'block';
    }
    else {
    card.style.display = 'none';
    }
    });
    });

  • 点击诸如”show all”的按钮,恢复过滤前的所有节点

    1
    2
    3
    4
    5
    6
    document.querySelector('#show-all').addEventListener('click', () => {
    document.querySelector('#search').value = '';
    Array.from(document.querySelectorAll('.job-card')).forEach((card) => {
    card.style.display = 'block';
    });
    });

Exercises Six

📺JavaScript DOM Exercises 02
🔗Codepen

  • 在已有表格上新增一行⭐⭐⭐

    1
    2
    3
    4
    5
    6
    7
    const newRow = document.createElement('tr');
    ['6', 'Sean', 'Reyes', '@sreyes'].forEach(text => {
    const newCol = document.createElement('td');
    newCol.innerText = text;
    newRow.appendChild(newCol);
    });
    document.querySelector('table').appendChild(newRow);

  • 修改列值为”XX”的行的信息

    1
    2
    3
    4
    5
    Array.from(document.querySelectorAll('table tr')).slice(1).forEach(row => {
    if (row.querySelector('td:nth-child(2)').innerText == 'Leona' && row.querySelector('td:nth-child(3)').innerText == 'Dixon') {
    row.querySelector('td:nth-child(4)').innerText = '@dixonl';
    }
    });

  • 将指定的一行移到指定位置,并重新整理序号⭐

    1
    2
    3
    4
    5
    6
    const rosa = document.querySelector('table tr:nth-child(5)');
    document.querySelector('table tr:nth-child(1)').insertAdjacentElement('afterEnd', rosa);
    // rosa.parentNode.insertBefore(row, document.querySelector('table tr:nth-child(2)'));
    Array.from(document.querySelectorAll('table tr td:nth-child(1)')).forEach((item, idx) => {
    item.innerText = idx + 1;
    });

  • 将指定的一列移到指定位置⭐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Array.from(document.querySelectorAll('table tr')).forEach(row => {
    const handleCol = row.querySelector('th:nth-child(4), td:nth-child(4)');
    const indexCol = row.querySelector('th:nth-child(1), td:nth-child(1)');
    indexCol.insertAdjacentElement('afterEnd', handleCol);
    });
    // const handleTh = document.querySelector('table tr th:nth-child(4)');
    // document.querySelector('table tr th:nth-child(1)').insertAdjacentElement('afterEnd', handleTh);
    // const handles = Array.from(document.querySelectorAll('table tr')).slice(1);
    // handles.forEach(row => {
    // const handle = row.querySelector('td:nth-child(4)');
    // row.querySelector('td:nth-child(1)').insertAdjacentElement('afterEnd', handle);
    // });

  • 为表格奇偶行添加不同样式

    1
    2
    3
    4
    5
    Array.from(document.querySelectorAll('table tr')).forEach((row, index) => {
    if (index % 2 === 0) {
    row.style['background-color'] = '#f2f2f2';
    }
    });


Summary

  1. Do not forget to convert “selectorAll” into “array”.
Author:WhiteBeerHouse
Link:https://github.com/WhiteBeerHouse/WhiteBeerHouse.github.io/tree/master/2021/04/05/DOM%20Manipulation%20Practice/
Copyright Notice:All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.