polyfill-001

forEach支持IE11+,classList支持IE10+。
forEach、classList、details、closest、NodeList forEach support、hasAttribute、before()、after()、replaceWith()、append()、prepend()、remove()、document.scrollingElement、resize、placeholder

forEach

MDN官方polyfill,兼容IE9+

if ( !Array.prototype.forEach ) {
  Array.prototype.forEach = function forEach( callback, thisArg ) {
    var T, k;
    if ( this == null ) {
      throw new TypeError( "this is null or not defined" );
    }
    var O = Object(this);
    var len = O.length >>> 0;
    if ( typeof callback !== "function" ) {
      throw new TypeError( callback + " is not a function" );
    }
    if ( arguments.length > 1 ) {
      T = thisArg;
    }
    k = 0;
    while( k < len ) {
      var kValue;
      if ( k in O ) {
        kValue = O[ k ];
        callback.call( T, kValue, k, O );
      }
      k++;
    }
  };
}

classList

兼容IE9+,包含add()remove()toggle()contains()items()

// polyfill classList
if (!("classList" in document.documentElement)) {
    Object.defineProperty(HTMLElement.prototype, 'classList', {
        get: function() {
            var self = this;
            function update(fn) {
                return function(value) {
                    var classes = self.className.split(/\s+/g),
                        index = classes.indexOf(value);
 
                    fn(classes, index, value);
                    self.className = classes.join(" ");
                }
            }
 
            return {
                add: update(function(classes, index, value) {
                    if (!~index) classes.push(value);
                }),
 
                remove: update(function(classes, index) {
                    if (~index) classes.splice(index, 1);
                }),
 
                toggle: update(function(classes, index, value) {
                    if (~index)
                        classes.splice(index, 1);
                    else
                        classes.push(value);
                }),
 
                contains: function(value) {
                    return !!~self.className.split(/\s+/g).indexOf(value);
                },
 
                item: function(i) {
                    return self.className.split(/\s+/g)[i] || null;
                }
            };
        }
    });
}

details

来源:https://github.com/javan/details-element-polyfill

// details/summary polyfill
    if (!('open' in document.createElement('details'))) {
        document.addEventListener('click', function (event) {
            var target = event.target;
            var elSummary = $(target).parents('summary');
            if (target.tagName == 'summary') {
                elSummary = $(target);
            }
            if (elSummary.length) {
                var elDetails = elSummary.parents('details');
                var open = elDetails.attr('open');
                if (typeof open == 'string') {
                    elDetails.removeAttr('open');
                } else {
                    elDetails.attr('open', '');
                }
            }
        });
    }
/*
Details Element Polyfill 2.4.0
Copyright © 2019 Javan Makhmali
 */
(function() {
  "use strict";
  var element = document.createElement("details");
  var elementIsNative = typeof HTMLDetailsElement != "undefined" && element instanceof HTMLDetailsElement;
  var support = {
    open: "open" in element || elementIsNative,
    toggle: "ontoggle" in element
  };
  var styles = '\ndetails, summary {\n  display: block;\n}\ndetails:not([open]) > *:not(summary) {\n  display: none;\n}\nsummary::before {\n  content: "►";\n  padding-right: 0.3rem;\n  font-size: 0.6rem;\n  cursor: default;\n}\n[open] > summary::before {\n  content: "▼";\n}\n';
  var _ref = [], forEach = _ref.forEach, slice = _ref.slice;
  if (!support.open) {
    polyfillStyles();
    polyfillProperties();
    polyfillToggle();
    polyfillAccessibility();
  }
  if (support.open && !support.toggle) {
    polyfillToggleEvent();
  }
  function polyfillStyles() {
    document.head.insertAdjacentHTML("afterbegin", "<style>" + styles + "</style>");
  }
  function polyfillProperties() {
    var prototype = document.createElement("details").constructor.prototype;
    var setAttribute = prototype.setAttribute, removeAttribute = prototype.removeAttribute;
    var open = Object.getOwnPropertyDescriptor(prototype, "open");
    Object.defineProperties(prototype, {
      open: {
        get: function get() {
          if (this.tagName == "DETAILS") {
            return this.hasAttribute("open");
          } else {
            if (open && open.get) {
              return open.get.call(this);
            }
          }
        },
        set: function set(value) {
          if (this.tagName == "DETAILS") {
            return value ? this.setAttribute("open", "") : this.removeAttribute("open");
          } else {
            if (open && open.set) {
              return open.set.call(this, value);
            }
          }
        }
      },
      setAttribute: {
        value: function value(name, _value) {
          var _this = this;
          var call = function call() {
            return setAttribute.call(_this, name, _value);
          };
          if (name == "open" && this.tagName == "DETAILS") {
            var wasOpen = this.hasAttribute("open");
            var result = call();
            if (!wasOpen) {
              var summary = this.querySelector("summary");
              if (summary) summary.setAttribute("aria-expanded", true);
              triggerToggle(this);
            }
            return result;
          }
          return call();
        }
      },
      removeAttribute: {
        value: function value(name) {
          var _this2 = this;
          var call = function call() {
            return removeAttribute.call(_this2, name);
          };
          if (name == "open" && this.tagName == "DETAILS") {
            var wasOpen = this.hasAttribute("open");
            var result = call();
            if (wasOpen) {
              var summary = this.querySelector("summary");
              if (summary) summary.setAttribute("aria-expanded", false);
              triggerToggle(this);
            }
            return result;
          }
          return call();
        }
      }
    });
  }
  function polyfillToggle() {
    onTogglingTrigger(function(element) {
      element.hasAttribute("open") ? element.removeAttribute("open") : element.setAttribute("open", "");
    });
  }
  function polyfillToggleEvent() {
    if (window.MutationObserver) {
      new MutationObserver(function(mutations) {
        forEach.call(mutations, function(mutation) {
          var target = mutation.target, attributeName = mutation.attributeName;
          if (target.tagName == "DETAILS" && attributeName == "open") {
            triggerToggle(target);
          }
        });
      }).observe(document.documentElement, {
        attributes: true,
        subtree: true
      });
    } else {
      onTogglingTrigger(function(element) {
        var wasOpen = element.getAttribute("open");
        setTimeout(function() {
          var isOpen = element.getAttribute("open");
          if (wasOpen != isOpen) {
            triggerToggle(element);
          }
        }, 1);
      });
    }
  }
  function polyfillAccessibility() {
    setAccessibilityAttributes(document);
    if (window.MutationObserver) {
      new MutationObserver(function(mutations) {
        forEach.call(mutations, function(mutation) {
          forEach.call(mutation.addedNodes, setAccessibilityAttributes);
        });
      }).observe(document.documentElement, {
        subtree: true,
        childList: true
      });
    } else {
      document.addEventListener("DOMNodeInserted", function(event) {
        setAccessibilityAttributes(event.target);
      });
    }
  }
  function setAccessibilityAttributes(root) {
    findElementsWithTagName(root, "SUMMARY").forEach(function(summary) {
      var details = findClosestElementWithTagName(summary, "DETAILS");
      summary.setAttribute("aria-expanded", details.hasAttribute("open"));
      if (!summary.hasAttribute("tabindex")) summary.setAttribute("tabindex", "0");
      if (!summary.hasAttribute("role")) summary.setAttribute("role", "button");
    });
  }
  function eventIsSignificant(event) {
    return !(event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.target.isContentEditable);
  }
  function onTogglingTrigger(callback) {
    addEventListener("click", function(event) {
      if (eventIsSignificant(event)) {
        if (event.which <= 1) {
          var element = findClosestElementWithTagName(event.target, "SUMMARY");
          if (element && element.parentNode && element.parentNode.tagName == "DETAILS") {
            callback(element.parentNode);
          }
        }
      }
    }, false);
    addEventListener("keydown", function(event) {
      if (eventIsSignificant(event)) {
        if (event.keyCode == 13 || event.keyCode == 32) {
          var element = findClosestElementWithTagName(event.target, "SUMMARY");
          if (element && element.parentNode && element.parentNode.tagName == "DETAILS") {
            callback(element.parentNode);
            event.preventDefault();
          }
        }
      }
    }, false);
  }
  function triggerToggle(element) {
    var event = document.createEvent("Event");
    event.initEvent("toggle", false, false);
    element.dispatchEvent(event);
  }
  function findElementsWithTagName(root, tagName) {
    return (root.tagName == tagName ? [ root ] : []).concat(typeof root.getElementsByTagName == "function" ? slice.call(root.getElementsByTagName(tagName)) : []);
  }
  function findClosestElementWithTagName(element, tagName) {
    if (typeof element.closest == "function") {
      return element.closest(tagName);
    } else {
      while (element) {
        if (element.tagName == tagName) {
          return element;
        } else {
          element = element.parentNode;
        }
      }
    }
  }
})();

closest

closest polyfill

if (!Element.prototype.matches) {
    Element.prototype.matches = Element.prototype.msMatchesSelector ||
                              Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
    Element.prototype.closest = function (s) {
        var el = this;

        do {
            if (el.matches(s)) return el;
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === 1);

        return null;
    };
}

NodeList forEach support

if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
}

hasAttribute

(function (prototype) {
    prototype.hasAttribute = prototype.hasAttribute || function (name) {
        return !!(this.attributes[name] &&
                  this.attributes[name].specified);
    };
})(Element.prototype);

before()

(function (arr) {
    arr.forEach(function (item) {
        if (item.hasOwnProperty('before')) {
            return;
        }
        Object.defineProperty(item, 'before', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: function before () {
                var argArr = Array.prototype.slice.call(arguments);
                var docFrag = document.createDocumentFragment();

                argArr.forEach(function (argItem) {
                    var isNode = argItem instanceof Node;
                    docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
                });

                this.parentNode.insertBefore(docFrag, this);
            }
        });
    });
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

after()

(function (arr) {
    arr.forEach(function (item) {
        if (item.hasOwnProperty('after')) {
            return;
        }
        Object.defineProperty(item, 'after', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: function after () {
                var argArr = Array.prototype.slice.call(arguments);
                var docFrag = document.createDocumentFragment();

                argArr.forEach(function (argItem) {
                    var isNode = argItem instanceof Node;
                    docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
                });

                this.parentNode.insertBefore(docFrag, this.nextSibling);
            }
        });
    });
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

replaceWith()

(function () {
    var ReplaceWith = function (Ele) {
        var parent = this.parentNode;
        var i = arguments.length;
        var firstIsNode = +(parent && typeof Ele === 'object');
        if (!parent) return;

        while (i-- > firstIsNode) {
            if (parent && typeof arguments[i] !== 'object') {
                arguments[i] = document.createTextNode(arguments[i]);
            } if (!parent && arguments[i].parentNode) {
                arguments[i].parentNode.removeChild(arguments[i]);
                continue;
            }
            parent.insertBefore(this.previousSibling, arguments[i]);
        }
        if (firstIsNode) parent.replaceChild(this, Ele);
    };

    if (!Element.prototype.replaceWith) {
        Element.prototype.replaceWith = ReplaceWith;
    }

    if (!CharacterData.prototype.replaceWith) {
        CharacterData.prototype.replaceWith = ReplaceWith;
    }
    if (!DocumentType.prototype.replaceWith) {
        CharacterData.prototype.replaceWith = ReplaceWith;
    }
})();

append()

(function (arr) {
    arr.forEach(function (item) {
        if (item.hasOwnProperty('append')) {
            return;
        }
        Object.defineProperty(item, 'append', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: function append() {
                var argArr = Array.prototype.slice.call(arguments);
                var docFrag = document.createDocumentFragment();

                argArr.forEach(function (argItem) {
                    var isNode = argItem instanceof Node;
                    docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
                });

                this.appendChild(docFrag);
            }
        });
    });
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

prepend()

(function (arr) {
    arr.forEach(function (item) {
        if (item.hasOwnProperty('prepend')) {
            return;
        }
        Object.defineProperty(item, 'prepend', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: function prepend() {
                var argArr = Array.prototype.slice.call(arguments);
                var docFrag = document.createDocumentFragment();

                argArr.forEach(function (argItem) {
                    var isNode = argItem instanceof Node;
                    docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
                });

                this.insertBefore(docFrag, this.firstChild);
            }
        });
    });
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

remove()

(function (arr) {
    arr.forEach(function (item) {
        if (item.hasOwnProperty('remove')) {
            return;
        }
        Object.defineProperty(item, 'remove', {
            configurable: true,
            enumerable: true,
            writable: true,
            value: function remove() {
                if (this.parentNode === null) {
                    return;
                }
                this.parentNode.removeChild(this);
            }
        });
    });
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

document.scrollingElement

https://mths.be/scrollingelement v1.5.2 by @diegoperini & @mathias | MIT license

if (!('scrollingElement' in document)) (function () {

    function computeStyle(element) {
        if (window.getComputedStyle) {
            // Support Firefox < 4 which throws on a single parameter.
            return getComputedStyle(element, null);
        }
        // Support Internet Explorer < 9.
        return element.currentStyle;
    }

    function isBodyElement(element) {
        // The `instanceof` check gives the correct result for e.g. `body` in a
        // non-HTML namespace.
        if (window.HTMLBodyElement) {
            return element instanceof HTMLBodyElement;
        }
        // Fall back to a `tagName` check for old browsers.
        return /body/i.test(element.tagName);
    }

    function getNextBodyElement(frameset) {
        // We use this function to be correct per spec in case `document.body` is
        // a `frameset` but there exists a later `body`. Since `document.body` is
        // a `frameset`, we know the root is an `html`, and there was no `body`
        // before the `frameset`, so we just need to look at siblings after the
        // `frameset`.
        var current = frameset;
        while (current = current.nextSibling) {
            if (current.nodeType == 1 && isBodyElement(current)) {
                return current;
            }
        }
        // No `body` found.
        return null;
    }

    // Note: standards mode / quirks mode can be toggled at runtime via
    // `document.write`.
    var isCompliantCached;
    var isCompliant = function () {
        var isStandardsMode = /^CSS1/.test(document.compatMode);
        if (!isStandardsMode) {
            // In quirks mode, the result is equivalent to the non-compliant
            // standards mode behavior.
            return false;
        }
        if (isCompliantCached === void 0) {
            // When called for the first time, check whether the browser is
            // standard-compliant, and cache the result.
            var iframe = document.createElement('iframe');
            iframe.style.height = '1px';
            (document.body || document.documentElement || document).appendChild(iframe);
            var doc = iframe.contentWindow.document;
            doc.write('<!DOCTYPE html><div style="height:9999em">x</div>');
            doc.close();
            isCompliantCached = doc.documentElement.scrollHeight > doc.body.scrollHeight;
            iframe.parentNode.removeChild(iframe);
        }
        return isCompliantCached;
    };

    function isRendered(style) {
        return style.display != 'none' && !(style.visibility == 'collapse' &&
            /^table-(.+-group|row|column)$/.test(style.display));
    }

    function isScrollable(body) {
        // A `body` element is scrollable if `body` and `html` both have
        // non-`visible` overflow and are both being rendered.
        var bodyStyle = computeStyle(body);
        var htmlStyle = computeStyle(document.documentElement);
        return bodyStyle.overflow != 'visible' && htmlStyle.overflow != 'visible' &&
            isRendered(bodyStyle) && isRendered(htmlStyle);
    }

    var scrollingElement = function () {
        if (isCompliant()) {
            return document.documentElement;
        }
        var body = document.body;
        // Note: `document.body` could be a `frameset` element, or `null`.
        // `tagName` is uppercase in HTML, but lowercase in XML.
        var isFrameset = body && !/body/i.test(body.tagName);
        body = isFrameset ? getNextBodyElement(body) : body;
        // If `body` is itself scrollable, it is not the `scrollingElement`.
        return body && isScrollable(body) ? null : body;
    };

    if (Object.defineProperty) {
        // Support modern browsers that lack a native implementation.
        Object.defineProperty(document, 'scrollingElement', {
            'get': scrollingElement
        });
    } else if (document.__defineGetter__) {
        // Support Firefox ≤ 3.6.9, Safari ≤ 4.1.3.
        document.__defineGetter__('scrollingElement', scrollingElement);
    } else {
        // IE ≤ 4 lacks `attachEvent`, so it only gets this one assignment. IE ≤ 7
        // gets it too, but the value is updated later (see `propertychange`).
        document.scrollingElement = scrollingElement();
        document.attachEvent && document.attachEvent('onpropertychange', function () {
            // This is for IE ≤ 7 only.
            // A `propertychange` event fires when `<body>` is parsed because
            // `document.activeElement` then changes.
            if (window.event.propertyName == 'activeElement') {
                document.scrollingElement = scrollingElement();
            }
        });
    }
}());

resize

resize polyfill for IE/Edge. @author zhangxinxu(.com)

if (typeof window.getComputedStyle(document.body).resize == 'undefined' && window.HTMLTextAreaElement) {
    HTMLTextAreaElement.prototype.setResize = function () {
        // 元素
        var textarea = this;
        var target = textarea.data && textarea.data.resize;
        var resize = null;
        // 文本域的id
        var id = textarea.id;
        if (!id) {
            id = ('r' + Math.random()).replace('0.', '');
            textarea.id = id;
        }
        // 获取resize属性值
        var attrResize = textarea.getAttribute('resize');

        if (typeof attrResize == 'string' && attrResize != 'vertical' && attrResize != 'horizontal') {
            attrResize = 'both';
        }
        if (typeof attrResize != 'string') {
            return;
        }

        // 创建模拟拉伸的基本元素
        if (!target) {
            target = document.createElement('span');
            resize = document.createElement('label');
            resize.setAttribute('for', id);
            target.appendChild(resize);
            // 一些固定的样式设置
            target.style.position = 'relative';
            target.style.verticalAlign = window.getComputedStyle(textarea).verticalAlign;

            resize.style.position = 'absolute';
            resize.style.width = '17px';
            resize.style.height = '17px';
            resize.style.background = "url(\"data:image/svg+xml,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M765.558 510.004a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0zM765.558 821.46a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0zM422.15700000000004 821.46a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0zM422.15700000000004 510.004a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0zM765.558 202.54a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0zM66.77700000000002 821.46a93.65 93.65 0 1 0 191.665 0 93.65 93.65 0 1 0-191.665 0z' fill='%23BFBFBF'/%3E%3C/svg%3E\") no-repeat center";
            resize.style.bottom = '0';
            resize.style.right = '0';
            resize.style.backgroundSize = '12px 12px';
            // 在textarea元素后面显示
            textarea.insertAdjacentElement('afterend', target);
            textarea.data = textarea.data || {};
            textarea.data.resize = target;

            // 事件
            var store = {};
            resize.addEventListener('mousedown', function (event) {
                store.resizing = true;
                store.startX = event.pageX;
                store.startY = event.pageY;
                // 此时textarea的尺寸
                store.offsetWidth = textarea.offsetWidth;
                store.offsetHeight = textarea.offsetHeight;

                event.preventDefault();
            });

            document.addEventListener('mousemove', function (event) {
                if (!store.resizing) {
                    return;
                }
                event.preventDefault();

                var currentX = event.pageX;
                var currentY = event.pageY;

                var moveX = currentX - store.startX;
                var moveY = currentY - store.startY;

                var currentWidth = store.offsetWidth + moveX;
                var currentHeight = store.offsetHeight + moveY;

                if (currentWidth < 40) {
                    currentWidth = 40;
                }
                if (currentHeight < 40) {
                    currentHeight = 40;
                }

                // 尺寸设置
                if (attrResize == 'both' || attrResize == 'horizontal') {
                    textarea.style.width = currentWidth + 'px';
                    if (target.style.display == 'block') {
                        target.style.width = currentWidth + 'px';
                    }
                }
                if (attrResize == 'both' || attrResize == 'vertical') {
                    textarea.style.height = currentHeight + 'px';
                    if (/inline/.test(styleDisplay)) {
                        target.style.height = currentHeight + 'px';
                    }
                }
            });

            document.addEventListener('mouseup', function () {
                if (store.resizing) {
                    store.resizing = false;
                }
            });
        }

        // 样式的控制与处理
        var styleDisplay = window.getComputedStyle(textarea).display;
        if (styleDisplay == 'none') {
            target.style.display = 'none';
        } else if (/inline/.test(styleDisplay)) {
            target.style.display = 'inline-block';
            target.style.height = textarea.offsetHeight + 'px';
        } else {
            target.style.display = 'block';
            target.style.width = textarea.offsetWidth + 'px';
        }
    };

    HTMLTextAreaElement.prototype.initResize = function () {
        // 避免重复初始化
        if (this.isInitResize) {
            return;
        }
        this.setResize();

        // 更新与处理
        this.addEventListener('DOMAttrModified', function () {
            this.setResize();
        }, false);

        this.isInitResize = true;
    };

    window.addEventListener('DOMContentLoaded', function () {
        document.querySelectorAll('textarea[resize]').forEach(function (textarea) {
            textarea.initResize();
        });

        // 插入内容时候的自动初始化
        document.body.addEventListener('DOMNodeInserted', function (event) {
            // 插入的元素
            var target = event.target;
            // 非元素节点不处理
            if (target.nodeType != 1) {
                return;
            }

            if (target.matches('textarea[resize]') && (!target.data || !target.data.resize)) {
                target.initResize();
            }
        });
    });
}

placeholder

zhangxinxu(.com)
only support one line no consideration of settings placeholder attr

if (!('placeholder' in document.createElement('input')) && window.HTMLTextAreaElement) {
    HTMLTextAreaElement.prototype.setPlaceholder = HTMLInputElement.prototype.setPlaceholder = function () {

        var control = this;

        var placeholder = control.getAttribute('placeholder');

        if (!placeholder) {
            control.style.backgroundPosition = '-2999px -2999px';
            return;
        }

        // 获取此时control的字体和字号
        var stylesControl = window.getComputedStyle(control);

        // 实现原理:创建一个offset screen canvas,并把placeholder绘制在上面

        // 一些样式
        var fontSize = stylesControl.fontSize;
        var fontFamily = stylesControl.fontFamily;
        var lineHeight = parseInt(stylesControl.lineHeight) || 20;

        // 起始坐标
        var x = parseInt(stylesControl.paddingLeft) || 0;
        var y = parseInt(stylesControl.paddingTop) || 0;

        // 尺寸
        var width = control.clientWidth;
        var height = control.offsetHeight;

        // 如果隐藏,则不处理
        var display = stylesControl.display;
        if (display == 'none' || width == 0) {
            return;
        }

        // canvas的创建
        control.data = control.data || {};
        // 先判断有没有缓存住
        var canvas = control.data.placeholder;
        // 如果没有,创建
        if (!canvas) {
            canvas = document.createElement('canvas');
            // 存储canvas对象
            control.data.placeholder = canvas;
        }

        // 如果尺寸没变化,placeholder也没变化,则不处理
        if (canvas.placeholder == placeholder && canvas.width == width) {
            return;
        }

        var context = canvas.getContext('2d');
        if (canvas.width) {
            context.clearRect(0, 0, canvas.width, canvas.height);
        }

        // 记住占位符内容
        canvas.placeholder = placeholder;

        // 尺寸变化
        canvas.width = width;
        canvas.height = height;

        // 设置样式
        context.fillStyle = '#a2a9b6';
        context.font = [fontSize, fontFamily].join(' ');
        context.textBaseline = 'top';

        // 字符分隔为数组
        var arrText = placeholder.split('');
        var line = '';
        var maxWidth = width - x * 2;

        for (var n = 0; n < arrText.length; n++) {
            var testLine = line + arrText[n];
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            if (testWidth > maxWidth && n > 0) {
                context.fillText(line, x, y);
                line = arrText[n];
                y += lineHeight;
            } else {
                line = testLine;
            }
        }
        context.fillText(line, x, y);

        var backgroundImage = canvas.toDataURL();
        control.style.backgroundRepeat = 'no-repeat';
        control.style.backgroundImage = 'url(' + backgroundImage + ')';
    };

    HTMLTextAreaElement.prototype.initPlaceholder = HTMLInputElement.prototype.initPlaceholder = function () {
        // 避免重复初始化
        if (this.isInitPlaceholder) {
            return;
        }

        this.setPlaceholder();

        // 更新与处理
        this.addEventListener('DOMAttrModified', function () {
            this.setPlaceholder();
        }, false);

        this.addEventListener('focus', function () {
            this.style.backgroundPosition = '-2999px -2999px';
        });
        this.addEventListener('blur', function () {
            if (this.value.trim() == '') {
                this.style.backgroundPosition = '';
            }
        });

        this.isInitPlaceholder = true;
    };

    window.addEventListener('DOMContentLoaded', function () {
        document.querySelectorAll('textarea[placeholder], input[placeholder]').forEach(function (control) {
            control.initPlaceholder();
        });

        // 插入内容时候的自动初始化
        document.body.addEventListener('DOMNodeInserted', function (event) {
            // 插入的Node节点
            var target = event.target;

            // 非元素节点不处理
            if (target.nodeType != 1) {
                return;
            }

            if (/^textarea|input$/i.test(target.tagName) && target.matches('[placeholder]')  && (!target.data || !target.data.placeholder)) {
                target.initPlaceholder();
            }
        });
    });
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • You Don't Need jQuery 前端发展很快,现代浏览器原生 API 已经足够好用。我们并不需要为了操...
    残丶梦阅读 1,416评论 0 2
  • github 前端发展很快,现代浏览器原生 API 已经足够好用。我们并不需要为了操作 DOM、Event 等再学...
    littleyu阅读 443评论 0 0
  • 前言:尽管现在有很多优秀的框架,大大简化了我们的DOM操作,但是我们仍然要学好DOM知识来写原生JS,从根本上去理...
    长鲸向南阅读 1,837评论 0 0
  • API集锦,需要用时查阅用。 一、节点1.1 节点属性 Node.nodeName //返回节点名称,只读Node...
    littleyu阅读 2,669评论 0 8
  • 原文 https://www.kancloud.cn/dennis/tgjavascript/241852 一、节...
    LuckyS007阅读 832评论 0 0