[SalesForce] Uncaught TypeError: a.getAttribute is not a function

Scenario:

I have a component that has an <aura:renderIf> within an <aura:iteration>. The <aura:iteration> references a component attribute called "selectedItems" to display a list of items, accompanied by a delete button. When the delete button is clicked, the following actions are performed on the Controller:

  1. var selectedItem = component.get('v.selectedItems');
  2. selectedItems.splice() the event.target.value
  3. component.set('v.selectedItems', selectedItems);

Problem:

Uncaught TypeError: a.getAttribute is not a function appears.

Other observations:

Aside from the error message, the delete button functions as expected.

Attempted fixes:

  1. I have tried to wrap either the <aura:renderIf> or the <aura:iteration> in a <span> without success as instructed in the stack exchange link below.

Lightning: Bug in Lightning framework when using aura:renderIf?

  1. When the code updating the component attribute is removed, there's no error, but the delete button does not function as desired.

  2. Changing splice() to pop() still shows the error, but only if the delete button of the last item is clicked. The delete button does not function as desired.

  3. The <aura:iteration> in the component was stripped down to just the delete button with an onclick controller event and a value attribute of an ID, while the controller was also stripped down to the component.get('v.selectedItems'), selectedItems.splice() based on event.target.value, then component.set(). Still shows the error even though the item is deleted.

Component

<aura:attribute name="selectedItems" type="sObject[]" /> 
....
<aura:iteration items="{!v.selectedItems}" var="obj">
    <tr>
        <td>
            <button type="button" class="btn btn-default" onclick="{!c.removeItem}" value="{!obj.x__Lookup_ID__c}">
                Del
            </button>
        </td>
    </tr>
</aura:iteration>

Controller

removeItem : function(component, event, helper) {
        var target = event.target.value;
        var selectedItems = component.get('v.selectedItems');
        
        for (var i = 0; i < selectedItems.length; i++) {
            if (target == selectedItems[i].x__Lookup_ID__c) {
                selectedItems.splice(i, 1);
            }
        }
        
        component.set('v.selectedItems', selectedItems);
    }

Question:

How do I prevent the uncaught typeerror from showing?

Update (2016-05-19):

I have five components in a Lightning App. All components use Bootstrap CSS.

Everything described and performed above this update section refers to Component 3 (selected item list).

Component 1 (client list) implements a Bootstrap Tab and uses Require JS to load Bootstrap JS in its controller (please see below). When the Bootstrap JS is removed, the Uncaught TypeError goes away, but the Bootstrap Tab no longer works. I wish to retain full Bootstrap functionality without the error message if possible.

require.config({
    paths: {
        "jquery": "/resource/ns__jquery?",
        "bootstrap": "/resource/ns__bootstrapjs?",
        "jqueryui": "/resource/ns__jquery_ui_js?",
        "bootstrapdatetimepickerjs": "/resource/ns__bootstrapdatetimepickerjs?"
    }
});

Update (2016-06-16):

The upgraded error message post Summer 16 update revealed the following.

Uncaught TypeError: a.getAttribute is not a function
throws at https://domain-dev-ed.lightning.force.com/resource/ns__jquery?:2:12385
    at Error (native)
    at Ky.<anonymous> (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:153:22)
    at new Ky (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:153:296)
    at Object.handler_aura_systemError (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:5058:225)
    at https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3512:244
    at Object.handleEvent (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3511:338)
    at handleDelegatedEvent (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3509:72)
    at G.oc (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:274:169)
    at https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:228:72
    at https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:264:424

In addition, removing the nested <aura:renderIf> does not make the error message go away.

Update (2016-06-22):

Also tried applying noConflict() with jQuery without success.

Of the five components that uses Bootstrap CSS, only the following components use Bootstrap JS: component 1 (client list) uses it for the tabs and component 4 (sub-items list) uses it for modals.

It was decided to customize Bootstrap JS by retaining only the aforementioned functionality. By doing this, the error message no longer appears (as of Summer 16) and the desired Bootstrap functionality remains intact.

Stripped Bootstrap JS

+function ($) {
  'use strict';

  // MODAL CLASS DEFINITION
  // ======================

  var Modal = function (element, options) {
    this.options             = options
    this.$body               = $(document.body)
    this.$element            = $(element)
    this.$dialog             = this.$element.find('.modal-dialog')
    this.$backdrop           = null
    this.isShown             = null
    this.originalBodyPad     = null
    this.scrollbarWidth      = 0
    this.ignoreBackdropClick = false

    if (this.options.remote) {
      this.$element
        .find('.modal-content')
        .load(this.options.remote, $.proxy(function () {
          this.$element.trigger('loaded.bs.modal')
        }, this))
    }
  }

  Modal.VERSION  = '3.3.6'

  Modal.TRANSITION_DURATION = 300
  Modal.BACKDROP_TRANSITION_DURATION = 150

  Modal.DEFAULTS = {
    backdrop: true,
    keyboard: true,
    show: true
  }

  Modal.prototype.toggle = function (_relatedTarget) {
    return this.isShown ? this.hide() : this.show(_relatedTarget)
  }

  Modal.prototype.show = function (_relatedTarget) {
    var that = this
    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })

    this.$element.trigger(e)

    if (this.isShown || e.isDefaultPrevented()) return

    this.isShown = true

    this.checkScrollbar()
    this.setScrollbar()
    this.$body.addClass('modal-open')

    this.escape()
    this.resize()

    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))

    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
      })
    })

    this.backdrop(function () {
      var transition = $.support.transition && that.$element.hasClass('fade')

      if (!that.$element.parent().length) {
        that.$element.appendTo(that.$body) // don't move modals dom position
      }

      that.$element
        .show()
        .scrollTop(0)

      that.adjustDialog()

      if (transition) {
        that.$element[0].offsetWidth // force reflow
      }

      that.$element.addClass('in')

      that.enforceFocus()

      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })

      transition ?
        that.$dialog // wait for modal to slide in
          .one('bsTransitionEnd', function () {
            that.$element.trigger('focus').trigger(e)
          })
          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
        that.$element.trigger('focus').trigger(e)
    })
  }

  Modal.prototype.hide = function (e) {
    if (e) e.preventDefault()

    e = $.Event('hide.bs.modal')

    this.$element.trigger(e)

    if (!this.isShown || e.isDefaultPrevented()) return

    this.isShown = false

    this.escape()
    this.resize()

    $(document).off('focusin.bs.modal')

    this.$element
      .removeClass('in')
      .off('click.dismiss.bs.modal')
      .off('mouseup.dismiss.bs.modal')

    this.$dialog.off('mousedown.dismiss.bs.modal')

    $.support.transition && this.$element.hasClass('fade') ?
      this.$element
        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
      this.hideModal()
  }

  Modal.prototype.enforceFocus = function () {
    $(document)
      .off('focusin.bs.modal') // guard against infinite focus loop
      .on('focusin.bs.modal', $.proxy(function (e) {
        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
          this.$element.trigger('focus')
        }
      }, this))
  }

  Modal.prototype.escape = function () {
    if (this.isShown && this.options.keyboard) {
      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
        e.which == 27 && this.hide()
      }, this))
    } else if (!this.isShown) {
      this.$element.off('keydown.dismiss.bs.modal')
    }
  }

  Modal.prototype.resize = function () {
    if (this.isShown) {
      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
    } else {
      $(window).off('resize.bs.modal')
    }
  }

  Modal.prototype.hideModal = function () {
    var that = this
    this.$element.hide()
    this.backdrop(function () {
      that.$body.removeClass('modal-open')
      that.resetAdjustments()
      that.resetScrollbar()
      that.$element.trigger('hidden.bs.modal')
    })
  }

  Modal.prototype.removeBackdrop = function () {
    this.$backdrop && this.$backdrop.remove()
    this.$backdrop = null
  }

  Modal.prototype.backdrop = function (callback) {
    var that = this
    var animate = this.$element.hasClass('fade') ? 'fade' : ''

    if (this.isShown && this.options.backdrop) {
      var doAnimate = $.support.transition && animate

      this.$backdrop = $(document.createElement('div'))
        .addClass('modal-backdrop ' + animate)
        .appendTo(this.$body)

      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
        if (this.ignoreBackdropClick) {
          this.ignoreBackdropClick = false
          return
        }
        if (e.target !== e.currentTarget) return
        this.options.backdrop == 'static'
          ? this.$element[0].focus()
          : this.hide()
      }, this))

      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow

      this.$backdrop.addClass('in')

      if (!callback) return

      doAnimate ?
        this.$backdrop
          .one('bsTransitionEnd', callback)
          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
        callback()

    } else if (!this.isShown && this.$backdrop) {
      this.$backdrop.removeClass('in')

      var callbackRemove = function () {
        that.removeBackdrop()
        callback && callback()
      }
      $.support.transition && this.$element.hasClass('fade') ?
        this.$backdrop
          .one('bsTransitionEnd', callbackRemove)
          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
        callbackRemove()

    } else if (callback) {
      callback()
    }
  }

  // these following methods are used to handle overflowing modals

  Modal.prototype.handleUpdate = function () {
    this.adjustDialog()
  }

  Modal.prototype.adjustDialog = function () {
    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight

    this.$element.css({
      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
    })
  }

  Modal.prototype.resetAdjustments = function () {
    this.$element.css({
      paddingLeft: '',
      paddingRight: ''
    })
  }

  Modal.prototype.checkScrollbar = function () {
    var fullWindowWidth = window.innerWidth
    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
      var documentElementRect = document.documentElement.getBoundingClientRect()
      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
    }
    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
    this.scrollbarWidth = this.measureScrollbar()
  }

  Modal.prototype.setScrollbar = function () {
    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
    this.originalBodyPad = document.body.style.paddingRight || ''
    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
  }

  Modal.prototype.resetScrollbar = function () {
    this.$body.css('padding-right', this.originalBodyPad)
  }

  Modal.prototype.measureScrollbar = function () { // thx walsh
    var scrollDiv = document.createElement('div')
    scrollDiv.className = 'modal-scrollbar-measure'
    this.$body.append(scrollDiv)
    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
    this.$body[0].removeChild(scrollDiv)
    return scrollbarWidth
  }


  // MODAL PLUGIN DEFINITION
  // =======================

  function Plugin(option, _relatedTarget) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.modal')
      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)

      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
      if (typeof option == 'string') data[option](_relatedTarget)
      else if (options.show) data.show(_relatedTarget)
    })
  }

  var old = $.fn.modal

  $.fn.modal             = Plugin
  $.fn.modal.Constructor = Modal


  // MODAL NO CONFLICT
  // =================

  $.fn.modal.noConflict = function () {
    $.fn.modal = old
    return this
  }


  // MODAL DATA-API
  // ==============

  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
    var $this   = $(this)
    var href    = $this.attr('href')
    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())

    if ($this.is('a')) e.preventDefault()

    $target.one('show.bs.modal', function (showEvent) {
      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
      $target.one('hidden.bs.modal', function () {
        $this.is(':visible') && $this.trigger('focus')
      })
    })
    Plugin.call($target, option, this)
  })

}(jQuery);

I don't want to call this case closed since this seemed like a lot of work to get around the problem than a real solution.

Best Answer

You should get the attribute value like this:

var target = event.target.getAttribute('value');
Related Topic