Skip to content

操作元素节点的 style 属性, 修改样式

因为 JavaScript 不能直接操作 css, 所以只能用操作 style 属性的方式来修改样式

html
<html lang="en">
  <head>
    <title>js</title>
  </head>
  <body>
    <div id="app"></div>

    <!-- javascript -->
    <script type="text/javascript">
      window.onload = function () {
        var app = document.getElementById("app");
        app.style.width = "200px";
        app.style.height = "300px";
        app.style.background = "black";
      };
    </script>
  </body>
</html>

注意: 特殊属性, js 的保留字 float 必须这样设置 cssFloat

getComputedStyle 获取元素的 css 样式(包括默认值)

CSSStyleDeclaration 是 CSS 属性键值对的集合, 是一个 Array-Like

html
<html lang="en">
  <head>
    <title>js</title>
  </head>
  <style>
    #app {
      width: 100px;
      height: 150px;
      background-color: red;
    }
  </style>
  <body>
    <div id="app"></div>

    <!-- javascript -->
    <script type="text/javascript">
      var app = document.getElementById("app");
      // console.log(app.style.width); // 通过style属性是获取不到的

      var appStyle = window.getComputedStyle(app); // 获取的是一个 CSSStyleDeclaration 对象
      console.log(appStyle.width); // 100px
    </script>
  </body>
</html>

这个方法是非常好用的, 但是很可惜万恶的 IE8 不支持这个方法

javascript
/**
 * get element computed styles
 * @returns {string|number|undefined}
 */
function getStyles(el, prop) {
  if (window.getComputedStyle) {
    getStyles = function (el, prop) {
      var styles = window.getComputedStyle(el, null);
      return styles[prop] ? styles[prop] : undefined;
    };
  } else {
    getStyles = function (el, prop) {
      // ie
      var styles = el.currentStyle;
      return styles[prop] ? styles[prop] : undefined;
    };
  }
  return getStyles(el, prop);
}

getComputedStyle 获取伪元素的样式

getComputedStyle 的第二个参数就是填写伪元素类型的, 填写其他的报错

html
<html lang="en">
  <head>
    <title>js</title>
  </head>
  <style>
    #app {
      width: 500px;
      height: 500px;
    }
    #app::before {
      content: "";
      width: 100px;
      height: 100px;
      background: red;
    }
    #app::after {
      content: "";
      width: 200px;
      height: 200px;
      background: blue;
    }
  </style>
  <body>
    <div id="app"></div>

    <!-- javascript -->
    <script type="text/javascript">
      var app = document.getElementById("app");
      var afterStyle = window.getComputedStyle(app, "after");
      var beforeStyle = window.getComputedStyle(app, "before");

      console.log("beforeStyle: ", beforeStyle.width); // 100px
      console.log("afterStyle: ", afterStyle.height); // 200px
    </script>
  </body>
</html>

大量操作元素的样式的策略

  1. style
  2. className(推荐)
html
<html lang="en">
  <head>
    <title>js</title>
  </head>

  <style>
    .some-styles {
      width: 100px;
      height: 200px;
      background: black;
    }
  </style>
  <body>
    <div id="box1"></div>
    <div id="box2"></div>
    <!-- javascript -->
    <script type="text/javascript"></script>

    <script type="text/javascript">
      var box1 = document.getElementById("box1"),
        box2 = document.getElementById("box2");

      // bad: 这样的代码很难维护
      box1.style.width = "100px";
      box1.style.height = "200px";
      box1.style.background = "black";

      // good: css和js代码是分开的, 容易理解和维护代码
      box2.classList.add("some-styles");
    </script>
  </body>
</html>

定时器动画

下拉菜单

html
<html lang="en">
  <head>
    <title>js</title>
  </head>

  <style>
    html,
    body,
    .dropdown,
    .main,
    .item {
      padding: 0;
      margin: 0;
      list-style: none;
    }
    .dropdown {
      width: 150px;
      margin: 20px;
    }
    .dropdown .trigger {
      border: none;
      width: 150px;
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      text-decoration: none;
      color: #fff;
      background-color: #222;
      position: relative;
      /* margin-bottom: 5px; */
    }
    .trigger::after {
      content: "v";
      position: absolute;
      top: 7px;
      right: 15px;
    }
    .active.trigger::after {
      content: "^" !important;
      font-size: 20px;
      top: 10px;
    }
    .dropdown .dropdown-menu {
      height: 0px;
      overflow: hidden;
      background-color: #555;
      color: #fff;
    }
    .dropdown-menu .menu-item {
      justify-content: center;
      align-items: center;
      display: flex;
      height: 40px;
    }
    .menu-item:hover {
      cursor: pointer;
      background-color: #222;
    }
  </style>
  <body>
    <div class="dropdown">
      <a href="javascript:;" class="trigger">下拉菜单</a>
      <div class="dropdown-menu">
        <div class="menu-item">项目1</div>
        <div class="menu-item">项目2</div>
        <div class="menu-item">项目3</div>
        <div class="menu-item">项目4</div>
        <div class="menu-item">项目5</div>
      </div>
    </div>
    <!-- javascript -->
    <script type="text/javascript"></script>

    <script>
      (function () {
        /**
         * 计算一个元素当前的样式
         */
        var getStyle = (function () {
          if (window.getComputedStyle) {
            return function (ele, prop) {
              var styles = window.getComputedStyle(ele, null);
              return styles[prop] ? styles[prop] : undefined;
            };
          }

          return function (el, prop) {
            // ie
            var styles = el.currentStyle;
            return styles[prop] ? styles[prop] : undefined;
          };
        })();

        var dropdown = document.getElementsByClassName("dropdown")[0],
          trigger = dropdown.getElementsByClassName("trigger")[0],
          menu = dropdown.getElementsByClassName("dropdown-menu")[0],
          menuHeight = 0,
          speed = 5,
          steps = 5,
          timer;

        /**
         * handle on mouse enter event
         */
        dropdown.onmouseenter = function () {
          // handle icon
          trigger.classList.add("active");

          // show menus animate
          var showMenu = function () {
            menuHeight = parseInt(getStyle(menu, "height")) || 0;
            menuHeight += steps;
            if (menuHeight >= 200) {
              menu.style.height = 200 + "px";
              timer && clearInterval(timer);
              return;
            }
            menu.style.height = menuHeight + "px";
          };

          timer && clearInterval(timer);
          timer = setInterval(showMenu, speed);
        };

        /**
         * handle on mouse leave event
         */
        dropdown.onmouseleave = function () {
          // handle icon
          trigger.classList.remove("active");

          // hide menus animate
          var hideMenu = function () {
            menuHeight = parseInt(getStyle(menu, "height")) || 0;
            menuHeight -= steps;
            if (menuHeight <= 0) {
              menu.style.height = "0px";
              timer && clearInterval(timer);
              return;
            }
            menu.style.height = menuHeight + "px";
          };

          timer && clearInterval(timer);
          timer = setInterval(hideMenu, speed);
        };
      })();
    </script>
  </body>
</html>

课后作业

淘宝首页无限轮播图 完成了无限轮播: 但是问题是: 第一张和最后一张不是连续的, 最后一张切换到第一张的时候有一段空白

index.html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>js</title>
    <link rel="stylesheet" href="./css/index.css" />
  </head>

  <style></style>
  <body>
    <div class="slider">
      <ul class="slider-wrapper">
        <li class="slider-item"><img src="./images/1.jpg" /></li>
        <li class="slider-item"><img src="./images/2.jpg" /></li>
        <li class="slider-item"><img src="./images/3.jpg" /></li>
        <li class="slider-item"><img src="./images/4.jpg" /></li>
        <li class="slider-item"><img src="./images/5.jpg" /></li>
        <li class="slider-item"><img src="./images/6.jpg" /></li>
      </ul>
    </div>
    <script src="./js/index.js"></script>
    <script>
      var slider = new Slider({
        slider: "slider-wrapper",
        showPoints: true,
      });
    </script>
  </body>
</html>

css/index.css

css
html,
body {
  margin: 0;
  padding: 0;
}

/* clear ul, li tag list-style */
.slider .slider-wrapper,
.slider .slider-item {
  list-style: none;
  margin: 0;
  padding: 0;
}

.slider {
  width: 500px;
  margin: 30px;
  position: relative;
  overflow: hidden;
}

.slider .slider-wrapper {
  width: 3000px;
  display: flex;
}

.slider-item img {
  width: 500px;
}
/* === */
.point-wrapper,
.point-item {
  margin: 0;
  padding: 0;
  list-style: none;
}

.point-wrapper {
  position: absolute;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.point-wrapper .point-item {
  width: 10px;
  height: 10px;
  margin: 0 5px;
  border-radius: 15px;
  background: #fff;
  opacity: 0.5;
}

.point-item.active,
.point-item:hover {
  background: #f00;
}

js/index.js

javascript
(function () {
  "use strict";

  /**
   * 计算一个元素当前的样式
   */
  var getStyle = (function () {
    if (window.getComputedStyle) {
      return function (ele, prop) {
        var styles = window.getComputedStyle(ele, null);
        return styles[prop] ? styles[prop] : undefined;
      };
    }
    return function (el, prop) {
      var styles = el.currentStyle;
      return styles[prop] ? styles[prop] : undefined;
    };
  })();

  function Slider(options) {
    var slider = document.getElementsByClassName(options.slider)[0];
    this.speed = options.speed || 2000;
    this.showPoints = options.showPoints || true;
    this.slider = slider;
    this.items = slider.children;
    this.count = slider.childElementCount;
    this.pointItems = [];
    if (this.showPoints) {
      this.pointItems = this.initPoints("point-wrapper", "point-item");
    }
    this.start();
    this.index = 0;
    this.timer;
    this.animateTimer;
  }

  // 初始化指示灯
  Slider.prototype.initPoints = function (pointClass, itemClass) {
    var mouseEnterHandler = (i) => {
      return () => {
        this.timer && clearInterval(this.timer);
        this.animateTimer && clearInterval(this.animateTimer);
        this.index = i;
        this.move();
      };
    };
    var mouseLeaveHandler = (i) => {
      this.index = i;
      return this.start.bind(this);
    };

    var pointWrapper = document.createElement("ul");
    pointWrapper.classList.add(pointClass);
    var item;
    var pointItems = [];
    for (var i = 0, len = this.count; i < len; i++) {
      item = document.createElement("li");
      item.classList.add(itemClass);
      (function (i) {
        item.onmouseenter = mouseEnterHandler(i);
        item.onmouseleave = mouseLeaveHandler(i);
      })(i);
      pointItems.push(item);
      pointWrapper.appendChild(item);
    }
    this.slider.appendChild(pointWrapper);
    pointItems[0].classList.add("active");
    return pointItems;
  };

  // 开启循环
  Slider.prototype.start = function () {
    this.timer && clearInterval(this.timer);
    this.timer = setInterval(() => {
      this.index += 1;
      if (this.index >= this.count) {
        this.index = 0;
      }
      this.move();
    }, this.speed);
  };

  // 切换动画
  Slider.prototype.move = function () {
    var sliderWidth = parseInt(getStyle(this.slider, "width")), // 总宽度
      itemWidth = sliderWidth / this.count, // 每一个item的宽度
      target = this.index * itemWidth, // 目标位置
      current = target - itemWidth; // 当前位置

    this.selectPoint();
    this.animateTimer && clearInterval(this.animateTimer);
    this.animateTimer = setInterval(() => {
      // start move animate
      current += 10;
      if (current >= target) {
        current = target;
        this.slider.style.marginLeft = -current + "px";
        this.animateTimer && clearInterval(this.animateTimer);
        return;
      }
      this.slider.style.marginLeft = -current + "px";
    }, 5);
  };

  // 选中一个指示灯
  Slider.prototype.selectPoint = function () {
    var items = this.pointItems;
    for (var i = 0, len = items.length; i < len; i++) {
      items[i].classList.remove("active");
    }
    items[this.index].classList.add("active");
  };

  window.Slider = Slider;
})();

Released under the MIT License.