Задать вопрос
@Sviaznoi

Как в SPA записать состояние в historyAPI при этом не отменить ветку при шаге назад?

Здравствуйте. Пишу проект для тренировки. Проект это одностраничное приложение которое я хочу написать только на js, css, html и node.js без фрэймворков и библиотек. Упёрся в момент связанный с событием popstate, конкретнее с возможностью сохранить прокрутку страницы в объекте state для каждой страницы. Я пытался делать это через pushState/replaceState но при шаге назад, используя эти команды, удаляются более свежие записи. Крутил варианты как можно перейти по истории браузера назад не используя эти команды, оказалось из стандартных инструментов браузера ничего подходящего нет, а react и т.п. в цели эксперимента не входит. В общем обрисую ситуацию:
Есть однострочное браузерное приложение. При загрузке нового контента создаётся снимок текущего состояния, включая прокрутку. При шаге назад меняю состояние прокрутки через replaceState. Всё это в скрипте main_data.js:
"use strict"

window.history.scrollRestoration = "manual";

let main = document.getElementsByTagName("main")[0],
    subMenu = document.querySelector("#subMenu");

window.addEventListener("popstate", async(e) => {
    history.replaceState({ name: cp(), scroll: { x: window.scrollX, y: window.scrollY }, path:window.location.pathname }, "", window.location.pathname);
    if (e.state) {
        switch(e.state.name){
            case 'main':                
                await res();
                break;
            default:               
                await contentF(e.state.name);
        }        
    }
    const { x, y } = e.state.scroll;
    window.scrollTo(x, y);
});

document.addEventListener("pointerdown", async (e) => {   
    if(e.target.hasAttribute("data-name")){
        let name = e.target.dataset.name;
        history.replaceState({ name: cp(), scroll: { x: window.scrollX, y: window.scrollY }, path:window.location.pathname }, "", window.location.pathname);        
        await contentF(name);        
        window.scrollTo(0, 0);        
    }
    if(e.target.classList.contains("main_content")){
        history.replaceState({ name: cp(), scroll: { x: window.scrollX, y: window.scrollY }, path:window.location.pathname }, "", window.location.pathname);
        await res();        
        window.scrollTo(0, 0);
    }
});

let contentF = async (name) => {
    let response = await fetch("/content", {
        method: 'POST',
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({name : name}),
    });

    if (response.ok) {
        const newDocument = await response.text();
        main.innerHTML = newDocument;
        history.pushState({name: name, scroll: { x: window.scrollX, y: window.scrollY}, path: "/content/" + name + "/"}, "", "/content/" + name + "/");
        if(subMenu.classList.contains("hidden")) subMenu.classList.remove("hidden");
        document.dispatchEvent(new CustomEvent("newContent", {bubbles:true}));        
    } else {
        console.error('Ошибка при отправке запроса:', response.status);
    }
}

let res = async () =>{
    let response = await fetch(`/?main=main`, {
        method: 'GET',
        headers: { "Content-Type": "application/json" },
    });
    if (response.ok) {
        let content = await response.json();
        main.innerHTML = "";
        content.forEach((item) => {
            main.innerHTML +=item;
        });
        history.poshState({name: "main", scroll: { x: window.scrollX, y: window.scrollY}, path: "/"}, "", "/");
        subMenu.classList.add("hidden");
        if(document.querySelector(".subUl")) {document.querySelector(".subUl").classList.add("hidden")};
    } else {
        console.log("Error download");
    }        
}

//let scrollP = (e) => {
//    const state = history.state || {};
//    
//  if (state.scroll &&  state.scroll.y !== 0) {
//    alert(state.scroll.y);
//   const { x, y } = state.scroll;
//   window.scrollTo(x, y);
//  } if (e == 'zero'){
//    alert('zero');
//   window.scrollTo(0, 0);
//  }
//};

let cp = () => {
    let currentPath = window.location.pathname,
        name,
        parts = currentPath.split('/');
    if (currentPath.startsWith('/content/') && parts.length >= 3 && parts[2] !== "") {
        name = parts[2];            
    } else {    
        name = "/";
    }
    return name;
}

export {res, contentF};


Ещё есть перезапись текущего history в скрипте start.js, который разведует в какой операционке он находится, что бы понять какой html и с каким css подгрузить в браузер - мобильная версия или пк? (Тоже часть эксперемента) :
"use strict"

async function getOS() {

            let loc = window.location.pathname,
                userAgent = window.navigator.userAgent,
                platform = window.navigator.platform,
                macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
                windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
                iosPlatforms = ['iPhone', 'iPad', 'iPod'],
                os = null,
                location;

            if (macosPlatforms.indexOf(platform) !== -1) {
                os = 'pc';
            } else if (iosPlatforms.indexOf(platform) !== -1) {
                os = 'mobile';
            } else if (windowsPlatforms.indexOf(platform) !== -1) {
                os = 'pc';
            } else if (/Android/.test(userAgent)) {
                os = 'mobile';
            } else if (!os && /Linux/.test(platform)) {
                os = 'pc';
            }

            switch(loc){
                case "/":
                    location = '';
                    break;
                default:
                    location = loc;    
            }
            
            let response = await fetch("https://localhost:3000"+loc, {
                method: 'POST',
                headers: {                  
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ os: os }), 
            });  
                if (response.ok) {
                    
                const newDocument = await response.text();
                document.open();
                document.write(newDocument);
                document.close();
                history.replaceState({name: "main",scroll: { x: window.scrollX, y: window.scrollY}, path:"https://localhost:3000"+loc},"", "https://localhost:3000"+loc);
            } else {
                console.error('Ошибка при отправке запроса:', response.status);
            }

        }
        window.onload = getOS;


Больше в history я ниоткуда не лез. Но если будет необходимость ознакомиться со всем проектом вот он на гидхабе: . Будет крайне обидно если нет такого способа. Мне даже нейронка не помогла с этим вопросом (BITO называется). У самого уже глаз замылился одни и те же варианты по кругу гоняю ничего в голову не идёт. Буду благодарен за помошь...
  • Вопрос задан
  • 29 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы