fill the input and select of form as last time inputed automatically

This script supports SPA like vue。

2025-06-26 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name            fill the input and select of form as last time inputed automatically
// @name:zh-CN 使用上次输入的值自动填写表格
// @namespace       https://greasyfork.org/users/3586-yechenyin
// @description     This script supports SPA like vue。
// @description:zh-CN  对所有网站生效,支持SPA(比如vue)动态插入的input和select。比如可以自动填写用户名和密码,自动点击同意协议。浏览器需要安装Tampermonkey或者Greasemonkey扩展,安卓手机浏览器推荐Yandex或者Kiwi浏览器。
// @include         http://*
// @include         https://*
// @exclude         https://www.baidu.com/*
// @exclude         https://www.google.com*
// @grant           GM_setValue
// @grant           GM_getValue
// @author          yechenyin
// @license         MIT
// @version         1.0.3
// ==/UserScript==
//闭包里的方法的用法和jquery同命名方法一致
(function () {
  var jQuery = function (selector) {
    return new jQuery.fn.init(selector)
  }
  jQuery.fn = jQuery.prototype = {
    length: 0,
    selector: '',
    init: function (elementOrSelector) {
      var nodes = []
      if (typeof (elementOrSelector) == 'string') {
        this.selector = elementOrSelector
        nodes = document.querySelectorAll(this.selector)
      } else if (elementOrSelector instanceof NodeList) {
        nodes = elementOrSelector
      } else if (elementOrSelector instanceof Element) {
        nodes = [elementOrSelector]
      }
      this.length = nodes.length
      for (var i = 0; i < nodes.length; i++) {
        this[i] = nodes[i]
      }
    },
    each: function (callback) {
      for (var i = 0; i < this.length; i++) {
        callback.call(this[i])
      }
    },
    on: function (event, callback) {
      this.each(function () {
        this.addEventListener(event, callback)
      })
    },
    text: function (string) {
      var i = 0
      if (string !== undefined) {
        for (i = 0; i < this.length; i++) {
          this[i].innerText = string
        }
      } else {
        return this[0].innerText
      }
    },
    val: function (value) {
      if (value === undefined) {
        var ret
        if (this[0].type == 'checkbox')
          ret = this[0].checked
        else if (this[0].type == 'radio' || this[0].type == 'text' || this[0].type == 'password' || this[0].tagName == 'select')
          ret = this[0].value
        return ret
      } else {
        for (var i = 0; i < this.length; i++) {
          if (this[i].type == 'checkbox' && Boolean(value))
            this[i].click()
          else if (this[i].type == 'radio')
            this[i].checked = this[i].value == value
          else if (this[i].type == 'text')
            this[i].value = value
          else if (this[i].type == 'password')
            this[i].value = value
          else if (this[i].tagName == 'select')
            this[i].value = value
          this[i].dispatchEvent(new Event('input', { bubbles: true }))
        }
      }
    },
    attr: function (attribute, value) {
      if (value === undefined) {
        return this[0].getAttribute(attribute)
      } else {
        this.each(function () {
          this.setAttribute(attribute, value)
        })
      }
    },
    click: function () {
      this[0].click()
    },
    find: function (selector) {
      var j = 0
      var result = []
      for (var i = 0; i < this.length; i++) {
        if (this[i].querySelectorAll(selector).length) {
        }
      }
    },
    append: function (html) {
      for (var i = 0; i < this.length; i++) {
        this[i].innerHTML += html
      }
    },
  }

  jQuery.fn.init.prototype = jQuery.fn
  if (!window.jQuery) window.jQuery = window.$ = jQuery
})()

//每当符合选择器规则的元素插入到页面中时,唤起callback方法。如果trigger_once为true,只唤起一次
jQuery.fn.inserted = function (callback, trigger_once = false) {
  var selector = this.selector;
  if ($(selector).length > 0) {
    //log($(selector).length + ' ' + selector + " is loaded at begin");
    callback.call($(selector));
  }
  var finished = false
  var recall = function (mutationsList, observer) {
    for (var i = 0; i < mutationsList.length; i++) {
      //log(mutationsList[i].target)
      if (mutationsList[i].addedNodes) {
        for (var j = 0; j < mutationsList[i].addedNodes.length; j++) {
          var element = mutationsList[i].addedNodes[j]
          if (!(trigger_once && finished) && element instanceof Element && element.querySelectorAll(selector).length) {
            var container = ''
            if (element.id)
              container = '#' + element.id
            else if (element.className)
              container = '.' + element.className
            else
              container = element.outerHtml
            //log(container + ' which contains ' + selector + ' is loaded')
            if (trigger_once) {
              observer.disconnect()
              finished = true
            }
            callback.call($(element.querySelectorAll(selector)))
          }
        }
      }
    }
  };
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  if (MutationObserver) {
    var observer = new MutationObserver(recall)
    observer.observe(document.body, {
      childList: true,
      subtree: true
    })
  }
}

//当符合选择器规则的元素插入到页面中时,调用一次callback方法,之后不会再唤起
jQuery.fn.loaded = function (callback) {
  if (callback)
    this.inserted(callback, true);
}

//每当元素类名变化时,唤起callback方法
jQuery.fn.onClassChanged = function (callback) {
  var recall = function (mutationsList, observer) {
    for (var i = 0; i < mutationsList.length; i++) {
      //log(mutationsList[i].target)
      if (mutationsList[i].type === 'attributes' && mutationsList[i].attributeName === 'class') {
        //callback.call($(mutationsList[i].target))
        callback.call(mutationsList[i].target)
      }
    }
  };
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  if (MutationObserver) {
    var observer = new MutationObserver(recall)
    var list = document.querySelectorAll(this.selector)
    for (var i = 0; i < list.length; i++) {
        observer.observe(list[i], { attributes: true })
    }
  }
}

//在元素加载完成后经过delay(单位毫秒)延迟后点击元素
jQuery.fn.clickAfterLoaded = function (delay = 0) {
  this.loaded(function () {
    setTimeout(function () {
      this[0].click()
    }.bind(this), delay)
  })
}
//在元素加载完成后经过delay(单位毫秒)延迟后设置输入值
jQuery.fn.setAfterLoaded = function (value, delay = 0) {
  this.loaded(function () {
    setTimeout(function () {
      this.val(value)
    }.bind(this), delay)
  })
}

//替代得到http成功回应后的回调
$.replaceResponseCallback = function (callback, continueOriginalCallback = false) {
  var open = XMLHttpRequest.prototype.open
  XMLHttpRequest.prototype.open = function () {
    this.addEventListener(
      'readystatechange',
      function () {
        if (
          this.readyState == 4 &&
          this.response
        ) {
          callback.call(this)
        }
      },
      false
    )
    if (continueOriginalCallback)
      open.apply(this, arguments)
    XMLHttpRequest.prototype.open = open
  }
}
//url改变后唤起callback,支持监测SPA的#后面的字符串变化
$.onUrlChange = function (callback) {
  history.pushState = ((f) =>
    function pushState() {
      var ret = f.apply(this, arguments)
      window.dispatchEvent(new Event('pushstate'))
      window.dispatchEvent(new Event('urlchange'))
      return ret
    })(history.pushState)

  history.replaceState = ((f) =>
    function replaceState() {
      var ret = f.apply(this, arguments)
      window.dispatchEvent(new Event('replacestate'))
      window.dispatchEvent(new Event('urlchange'))
      return ret
    })(history.replaceState)

  window.addEventListener('popstate', () => {
    window.dispatchEvent(new Event('urlchange'))
  })
  window.addEventListener('urlchange', function () {
    callback()
  })
}

jQuery.fn.saveChangedValue = function () {
  var that = this
  that.on('change', function () {
    //log(that)
    var href = 'inputed_' + location.href.replace(/\?.*/, '')
    var inputs = ''
    if (typeof GM_getValue === 'undefined')
      inputs = localStorage[href]
    else if (GM_getValue(href)) {
      inputs = GM_getValue(href)
    }
    if (inputs)
      inputs = JSON.parse(inputs)
    else
      inputs = {}
    //log(this.constructor.name)
    //log(Object.keys(this))
    var name = ''
    for (var i = 0; i < that.length; i++) {
        if (this == that[i])
            name = i
    }
    inputs[name] = $(this).val()
    log(name, inputs[name])
    if (typeof GM_setValue === 'undefined')
      localStorage[href] = JSON.stringify(inputs)
    else
      GM_setValue(href, JSON.stringify(inputs))
    //log(GM_getValue(href))
  })
}
jQuery.fn.recoverSavedValue = function () {
  this.inserted(function () {
    var that = this
    var href = 'inputed_' + location.href.replace(/\?.*/, '')
    var inputs = ''
    if (typeof GM_getValue === 'undefined')
      inputs = localStorage[href]
    else if (GM_getValue(href)) {
      inputs = GM_getValue(href)
    }
    if (inputs)
      inputs = JSON.parse(inputs)
    else
      inputs = {}
    log(inputs)
    if (Object.keys(inputs).length) {
      this.each(function () {
        var name = ''
        for (var i = 0; i < that.length; i++) {
            if (this == that[i])
                name = i
        }
        log(name)
        if (inputs.hasOwnProperty(name)) {
          $(this).val(inputs[name])
        }
      })
    }
  })
}

function log(info) {
    var printInConsole = false;
    if (printInConsole) {
        console.log(info)
    }
}

window.onload = function () {
  $('input, select').recoverSavedValue()
  $('input, select').inserted(function () {
    $('input, select').saveChangedValue()
    $('.el-select-dropdown__item').onClassChanged(function () {
        if (this.classList.contains('selected')) {
            log(this.innerText)
        }
    })
  })
}