Доброго времени суток, друзья!

В этой статье я хочу поделиться с Вами некоторыми находками, сделанными мной в процессе изучения JavaScript. Возможно, Вы найдете для себя что-то интересное. Данная часть серии будет посвящена, преимущественно, работе с DOM.

Рекомендую применить к body следующие стили:

body {
    margin: 0;
    min-height: 100vh;
    overflow: hidden;
}

Если планируете тестировать примеры, используйте эту функцию для очистки документа:

const clear = () => document.body.innerHTML = ''
clear()

1. Активный элемент


Свойство activeElement позволяет получить элемент, находящийся в фокусе.

const input = document.createElement('input')
input.setAttribute('type', 'text')
input.setAttribute('placeholder', 'Введите свое имя')
input.className = 'username'
document.body.append(input)

input.focus()

console.log(document.activeElement)
// <input type="text" placeholder="Введите свое имя" class="username">

2. Редактирование страницы


Свойство designMode позволяет редактировать страницу, открытую в браузере.

document.designMode = 'on'



3. Стили элемента


Метод getComputedStyle() позволяет получить стили элемента. Для получения определенного свойства следует использовать getPropertyValue().

// напишем вспомогательную функцию для получения определенного свойства элемента
// мы будем использовать ее в нескольких примерах
const getStyle = (element, property) => getComputedStyle(element).getPropertyValue(property)

// возьмем инпут из первого примера
// и определим его ширину и высоту
const inputWidth = getStyle(input, 'width')
const inputHeight = getStyle(input, 'height')
console.log(`Ширина: ${inputWidth}\nВысота: ${inputHeight}`)
// Ширина: 156.8px
// Высота: 16px

// позиционируем элемент, используя полученные данные
// предположим, что мы собираемся анимировать элемент
// поэтому не хотим использовать transform: translate(-50%, -50%)
// допустим также, что мы не знаем размеров элемента
// поэтому не можем использовать calc(50% - ширина/высота элемента)
input.setAttribute('style',
    `position: absolute; top: calc(50% - ${inputHeight.replace('px', '') / 2}px); left: calc(50% - ${inputWidth.replace('px', '') / 2}px);`
)

4. Определение браузера


Несмотря на наличие свойства userAgent объекта Navigator, получить корректную информацию о браузере пользователя не так-то просто. Решения типа if (navigator.userAgent.indexOf()) или if (navigator.userAgent.search()) не работают. Одним из работоспособных решений является использование библиотеки Detect.js. Скачиваем detect.min.js и подключаем его в head.

const ua = detect.parse(navigator.userAgent)
console.log(`${ua.os.family}\n${ua.browser.family}`)
/*
Windows 8.1
Chrome
*/

5. Получение координат


Объект Navigator также позволяет получить информацию о местонахождении пользователя, т.е. его координаты.

const success = position => {
    // деструктурируем объект
    const {
        latitude,
        longitude,
        altitude,
        speed
    } = position.coords
    console.log(`${latitude.toFixed(2)}\n${longitude.toFixed(2)}\n${altitude}\n${speed}`)

    // об этом ниже
    getCityAndWeather(latitude, longitude)
}

navigator.geolocation.getCurrentPosition(success)
/*
    56.90
    60.63
    null
    null
*/

// вот как мы можем использовать полученные данные
// определим город пользователя и погоду
const getCityAndWeather = (latitude, longitude) => {
    // прокси для преодоления CORS
    const proxy = 'https://cors-anywhere.herokuapp.com/'
    // данный сервис был куплен Facebook и станет платным в 2021 году
    const api = `${proxy}https://api.darksky.net/forecast/fd9d9c6418c23d94745b836767721ad1/${latitude}, ${longitude}`

    fetch(api)
        .then(response => response.json())
        .then(data => {
            console.log(data) // много всего

            // получаем город
            const city = data.timezone

            // получаем температуру
            const { temperature, summary } = data.currently

            // переводим фаренгейт в цельсий
            const celsius = Math.floor((temperature - 32) * (5 / 9)).toFixed()

            // выводим результат
            console.log(
                `${city}\n${celsius}°C\n${summary}`
            )
            /*
                Asia/Yekaterinburg
                15°C
                Overcast
            */
        })
}

6. Получение элементов


Как нам получить все элементы DOM? Использовать рекурсию.

const template = 
`<div>
    <p> Lorem ispum
        <span>dolor sit amet</span>
    </p>
</div>
<a href="#">link</a>`

document.body.innerHTML = template

const getElements = element => {
    for (const i of element.children) {
        console.log(i.tagName)

        // 0 -> ! -> false -> ! -> true
        if (!!i.children.length) {
            console.log('дочерний элемент')
            getElements(i)
        }
    }
}
getElements(document.body)
/*
    DIV
    дочерний элемент
    P  
    дочерний элемент
    SPAN
    A
*/

7. Разбор URL


Как нам получить отдельные части URL? Это можно сделать двумя способами.

// с помощью регулярки
const regex = /(\w+):\/\/([\w.]+)\/(\S*)/
const url = 'https://example.com/index.html'
const result = url.match(regex)
// полный адрес (абсолютный путь), протокол, хост, страница
console.log(result[0], result[1], result[2], result[3])
// https://example.com/index.html https example.com index.html

// с помощью конструктора URL
const url2 = new URL('https://example.com/search?query=fetch&page=2#awesome-page')
console.log(url2) // много всего

const {
    origin,
    protocol,
    host,
    pathname
} = url2
console.log(
    `${origin} ${protocol} ${host} ${pathname}`
)
// https://example.com https: example.com /search

// рекомендую почитать про свойство searchParams
// searchParams.get(), searchParams.append(), searchParams.has(), searchParams.delete() и т.д.

8. Позиционирование одного элемента относительно другого


const toCenter = (element, parent) => {
    element.style.position = 'relative'

    element.style.left = (parent.clientWidth - element.offsetWidth) / 2 + 'px'
    element.style.top = (parent.clientHeight - element.offsetHeight) / 2 + 'px'
}
const div = document.createElement('div')
div.setAttribute('style', 'width: 150px; height: 150px; background: red;')
document.body.append(div)

const div2 = document.createElement('div')
div2.setAttribute('style', 'width: 100px; height: 100px; background: green;')
div.append(div2)

const div3 = document.createElement('div')
div3.setAttribute('style', 'width: 50px; height: 50px; background: blue;')
div2.append(div3)

toCenter(div, document.body)
toCenter(div2, div)
toCenter(div3, div2)


9. Ширина и высота документа


Как нам получить полную ширину и высоту документа?

const pageWidth = Math.max(
    document.body.scrollWidth, document.documentElement.scrollWidth,
    document.body.offsetWidth, document.documentElement.offsetWidth,
    document.body.clientWidth, document.documentElement.clientWidth
)
const pageHeight = Math.max(
    document.body.scrollHeight, document.documentElement.scrollHeight,
    document.body.offsetHeight, document.documentElement.offsetHeight,
    document.body.clientHeight, document.documentElement.clientHeight
)

// один из вариантов использования
// определяем центр страницы
const pageCenter = [pageWidth / 2, pageHeight / 2]
console.log(pageCenter)

// создаем элемент для позиционирования
const p = document.createElement('p')
p.textContent = 'Lorem ipsum dolor sit amet'
document.body.append(p)
p.style.position = 'absolute'

// получаем ширину и высоту элемента, используя getStyle
const elementWidth = getStyle(p, 'width').replace('px', '')
const elementHeight = getStyle(p, 'height').replace('px', '')

// определяем центр элемента
const elementCenter = [elementWidth / 2, elementHeight / 2]
console.log(elementCenter)

// позиционируем элемент
p.style.top = pageCenter[1] - elementCenter[1] + 'px'
p.style.left = pageCenter[0] - elementCenter[0] + 'px'

10. Координаты элемента в контексте документа


Метод getBoundingClientRect() возвращает размер элемента и его позицию относительно области просмотра.

// возьмем p из предыдущего примера
console.log(p.getBoundingClientRect()) // много всего
console.log(
    `Отступ сверху => ${p.getBoundingClientRect().top.toFixed()}\nОтступ слева => ${p.getBoundingClientRect().left.toFixed()}`
)
/*
    Отступ сверху => 352
    Отступ слева => 288
*/

// создадим два элемента
// и определим, в какой части страницы находится каждый из них
const div = document.createElement('div')
div.setAttribute('style', 'width: 100px; height: 100px; background: #222; position: absolute; top: calc(50% - 50px); left: calc(25% - 50px);')
document.body.append(div)

const div2 = document.createElement('div')
div2.setAttribute('style', 'width: 100px; height: 100px; background: #222; position: absolute; top: calc(50% - 50px); left: calc(75% - 50px);')
document.body.append(div2)

document.querySelectorAll('div').forEach(div => div.addEventListener('click', event => {
    const x = event.target.getBoundingClientRect().x
    const width = event.target.getBoundingClientRect().width

    // расчеты приблизительные
    x + width < innerWidth / 2
    ? console.log('Элемент находится в левой части страницы.')
    : console.log('Элемент находится в правой части страницы.')
}))

div.click() // Элемент находится в левой части страницы.
div2.click() // Элемент находится в правой части страницы.

// определим расстояние между ними
const distanceBetweenDivs = (div, div2) =>
console.log((div2.getBoundingClientRect().x - div.getBoundingClientRect().x + div.getBoundingClientRect().width).toFixed())

distanceBetweenDivs(div, div2) // 477

11. Координаты курсора


Как нам получить координаты курсора? Очень просто.

// document.addEventListener('click', ev => console.log(`X => ${ev.clientX}\nY => ${ev.clientY}`))
/*
    X => 348
    Y => 304
*/

// вот как мы можем это использовать
// создаем холст и получаем его контекст
const canvas = document.createElement('canvas')
document.body.append(canvas)
const $ = canvas.getContext('2d')

// размер холста - область просмотра
canvas.width = innerWidth
canvas.height = innerHeight

// создаем вспомогательную функцию для получения случайного целого числа в заданном диапазоне
const randomInt = (min, max) => Math.floor(min + Math.random() * (max + 1 - min))

// создаем вспомогательную функцию для получения случайного цвета
const randomColor = () => `#${((Math.random()*0xfff)<<0).toString(16)}`

// давайте порисуем
// рисование фигур осуществляется по клику
// центр фигуры - место клика
// форма фигуры - круг или квадрат
let i = 0

canvas.addEventListener('click', ev => {
    $.beginPath()
    // если i - четное число, то рисуем круг
    // если нечетное - квадрат
    if (i % 2 === 0) {
        // $.arc(x, y, радиус, угол)
        $.arc(ev.clientX, ev.clientY, randomInt(10, 30), 0, 2 * Math.PI)
        $.fillStyle = randomColor()
        $.fill()
    } else {
        let randomSize = randomInt(20, 60)

        $.fillStyle = randomColor()
        // $.fillRect(x, y, ширина, высота)
        $.fillRect(ev.clientX - randomSize / 2, ev.clientY - randomSize / 2, randomSize, randomSize)
    }
    $.closePath()

    i++
})

Напоследок реализуем функцию рисования определенного количества фигур.

const manyShapes = number => {
    // очищаем холст
    $.clearRect(0, 0, canvas.width, canvas.height)

    for (let i = 0; i < number; i++) {
        let randomX = randomInt(0, innerWidth)
        let randomY = randomInt(0, innerHeight)
        if (i % 2 === 0) {
            $.beginPath()
            $.arc(randomX, randomY, randomInt(10, 30), 0, 2 * Math.PI)
            $.fillStyle = randomColor()
            $.fill()
        } else {
            let randomSize = randomInt(20, 60)
            $.beginPath()
            $.rect(randomX, randomY, randomSize, randomSize)
            $.fillStyle = randomColor()
            $.fill()
        }
    }
}
manyShapes(100)



Благодарю за потраченное время. Надеюсь, оно было потрачено не зря.

Продолжение следует…