Skip to content

React

react.html

html
<!DOCTYPE html>
<html lang="en" class="w-full h-full">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>React</title>

  <script src="https://cdn.tailwindcss.com"></script>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body class="w-full h-full">
<div id="root" class="w-full h-full"></div>

<script type="text/babel">
  const IMAGES_PATH = [
    './images/1.jpg',
    './images/2.jpg',
    './images/3.jpg',
    './images/4.jpg',
    './images/5.jpg',
  ];
  const MAX_SIZE = IMAGES_PATH.length;

  const useSlider = ({maxSize}) => {
    const [currentPage, setCurrentPage] = React.useState(1);

    const handlePrevPage = () => {
      setCurrentPage(prev => {
        return prev === 1 ? maxSize : prev - 1;
      });
    };
    const handleNextPage = () => {
      setCurrentPage(prev => {
        return prev === maxSize ? 1 : prev + 1;
      })
    };

    return {
      currentPage,
      handlePrevPage,
      handleNextPage,
    }
  };

  const Button = ({onClick, children}) => {
    return (
      <button
        className="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
        onClick={onClick}>
        {children}
      </button>
    )
  }

  const SliderItem = ({path}) => {
    return (
      <div className="w-full p-2 shrink-0 flex justify-center items-center">
        <img src={path} className="max-w-full max-h-full"/>
      </div>
    )
  };

  const MyApp = () => {
    const {currentPage, handlePrevPage, handleNextPage} = useSlider({maxSize: MAX_SIZE});

    return <>
      <div className="flex justify-center items-center gap-x-3 text-lg p-2">
        <Button onClick={handlePrevPage}>
          Prev
        </Button>
        <span>{currentPage} / {MAX_SIZE}</span>
        <Button onClick={handleNextPage}>
          Next
        </Button>
      </div>

      <div className="max-h-[500px] w-[100%] h-[50%] bg-[#ddd] overflow-hidden">
        <div className={`-translate-x-[${(currentPage - 1) * 100}%] w-full h-full flex duration-100`}>
          {
            IMAGES_PATH
              .map((path) => <SliderItem path={path} key={path} />)
          }
        </div>
      </div>
    </>;
  }

  const container = document.getElementById('root');
  const root = ReactDOM.createRoot(container);
  root.render(<MyApp />);
</script>
</body>
</html>

Vanila Js

index.html

html
<!doctype html>
<html lang="en" class="w-full h-full">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="w-full h-full">

<div class="flex justify-center items-center gap-x-3 text-lg p-2">
  <button
    class="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
    onclick="slider.prevPage()">
    Prev
  </button>
  <span id="status"></span>
  <button
    class="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
    onclick="slider.nextPage()">
    Next
  </button>
</div>

<div class="max-h-[500px] w-[100%] h-[50%] bg-[#ddd] overflow-hidden">
  <div id="slider" class="w-full h-full flex duration-100">
    <!-- 렌더링 영역 -->
  </div>
</div>

<script type="module" src="main.js"></script>
</body>
</html>

main.js

js
import {IMAGES_PATH, MAX_SIZE} from "./constants.js";
import {createSlider} from "./createSlider.js";
import {$, assign} from "./utils.js";

const changeSliderPage = currentPage => {
  const sliderPages = $('#slider');
  const {originClass} = sliderPages.dataset;

  assign(sliderPages, {
    className: `-translate-x-[${(currentPage - 1) * 100}%] ${originClass}`
  });
};
const changeStatus = currentPage => {
  assign($("#status"), {
    textContent: `${currentPage} / ${MAX_SIZE}`
  })
};

window.onload = () => {
  const sliderContent = IMAGES_PATH
    .map((path) => {
      return `<div class="w-full p-2 shrink-0 flex justify-center items-center">
          <img src="${path}" class="max-w-full max-h-full" />
        </div>`
    })
    .join('');

  const sliderPages = $('#slider');
  assign(sliderPages, {
    innerHTML: sliderContent
  });
  assign(sliderPages.dataset, {
    originClass:
      sliderPages.dataset.originClass ?? sliderPages.className
  });

  window.slider = createSlider({maxSize: MAX_SIZE});
  slider.watchState(changeSliderPage);
  slider.watchState(changeStatus);
};

createSlider.js

js
export const createSlider = ({maxSize}) => {
  const state = {
    watchers: [],
    currentPage: 1,
    maxSize,
  };

  const runWatcher = (watcher) => {
    watcher(state.currentPage);
  };

  const runWatchers = () => {
    state.watchers.forEach(runWatcher);
  };

  const watchState = (watcher) => {
    state.watchers.push(watcher);
    runWatcher(watcher);
  };

  const mutations = {
    nextPage: () => {
      if (state.currentPage === state.maxSize) {
        state.currentPage = 1;
      } else {
        state.currentPage += 1;
      }
      runWatchers();
    },
    prevPage: () => {
      if (state.currentPage === 1) {
        state.currentPage = state.maxSize;
      } else {
        state.currentPage -= 1;
      }
      runWatchers();
    },
  };

  return {
    ...mutations,
    watchState,
  }
};

utils.js

js
export const $ = selector => document.querySelector(selector);
export const assign = Object.assign;

constants.js

js
export const IMAGES_PATH = [
  './images/1.jpg',
  './images/2.jpg',
  './images/3.jpg',
  './images/4.jpg',
  './images/5.jpg',
];
export const MAX_SIZE = IMAGES_PATH.length;