angular.element与document.getElementById或带有旋转(繁忙)控件的jQuery选择器的比较。

162

我正在使用“Angularised”版本的旋转控件,文档在这里:http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

我不喜欢所展示解决方案中服务中使用 jQuery 实现将旋转控件附加到 DOM 元素的方法。我希望使用 Angular 的结构来访问该元素。我还想避免在服务中“硬编码”spinner需要附加到的元素的 id,并使用指令来设置 id 在服务(单例)中,以便服务的其他用户或服务本身不需要知道。

我正在研究 angular.element 相对于在相同元素 id 上使用 document.getElementById 给我们带来了什么区别。 例如,这个可以工作:

  var target = document.getElementById('appBusyIndicator');

这些都不行:

  var target = angular.element('#appBusyIndicator');
  var target = angular.element('appBusyIndicator');

我显然在做一些非常明显的错误!有谁能帮忙吗?

假设我可以解决上述问题,我还有一个类似的问题,尝试替换jQuery访问元素的方式:

例如: $(target).fadeIn('fast');可以工作,但是angular.element('#appBusyIndicator').fadeIn('fast')或者angular.element('appBusyIndicator').fadeIn('fast')不行。

有人可以指向一个好的文档示例,澄清使用Angular“element”和DOM元素的区别吗? Angular显然用自己的属性,方法等“包装”元素,但往往很难获得原始值。例如,如果我有一个<input type='number'>字段,并且我想访问在用户键入“--”(不带引号)时在UI中可见的原始内容,则什么都没有返回,可能是因为“type = number”意味着Angular拒绝了输入,即使它在UI中可见,我也想看到它,以便我可以测试并清除它。

任何提示/答案都将不胜感激。

谢谢。


5
angular.element是一个jQlite对象,如果你正在加载jQuery,则它也可以是一个jQuery对象。 - Neil
10个回答

234

它可以像这样工作:

var myElement = angular.element( document.querySelector( '#some-id' ) );

你需要将 Document.querySelector() 原生 Javascript 的调用封装到 angular.element() 调用中。因此,您始终会得到一个 jqLitejQuery 对象中的元素,具体取决于是否可用/加载了 jQueryangular.element 的官方文档:

如果可用,jQueryangular.element 的别名。如果 jQuery 不可用,则 angular.element 委托给 Angular 内置的 jQuery 子集,称为“jQuery lite”或 jqLite

Angular 中的所有元素引用始终使用 jQueryjqLite(例如指令编译或链接函数中的元素参数)进行包装。它们永远不是原始的 DOM 引用。

如果您想知道为什么要使用document.querySelector(),请阅读这个答案


42
有什么理由使得一个人更喜欢使用document.querySelector('#...')而不是简单地使用document.getElementById()呢? - Kato
4
你也可以在一个 Angular 元素上执行这个操作:var elDivParent = angular.element( elem[0].querySelector('#an-id'));,其中elem是一个 Angular 元素。这样可以在元素内进行搜索,对于单元测试很有用。 - unludo
4
@Kato 更多信息请参考这个答案。希望能够帮助到您。 - kaiser
使用Google Maps API,这个代码可以正常工作:var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );。感谢@kaiser的提示。由于某种原因,当我使用angular.element(document.querySelector('#address'))时,地图API抱怨我传递的第一个参数不是输入框,但最终一切都好了。 - nymo

49

如果您还没有阅读过Angular element文档,建议先进行阅读,以便了解jqLite支持哪些内容以及不支持哪些内容 - jqLite是内置在Angular中的jQuery子集。

仅使用jqLite时,那些按id选择器将不起作用,因为它们不受支持。

  var target = angular.element('#appBusyIndicator');
  var target = angular.element('appBusyIndicator');

因此,有两种选择:

  • 你可以仅使用jqLite,它比jQuery功能更为有限,但在大多数情况下已经足够。
  • 或者你可以在应用程序中包含完整的jQuery库,并像普通的jQuery一样在真正需要jQuery的地方使用它。

编辑:请注意,在加载angularJS之前应加载jQuery以优先使用jqLite:

  

如果在DOMContentLoaded事件触发之前加载了jQuery,则真实的jQuery始终优先于jqLite。

编辑2:我之前错过了问题的第二部分:

<input type="number">的问题,我认为这不是一个Angular问题,而是本地html5数字元素的预期行为。

即使你尝试使用jquery的.val()或原始的.value属性检索该值,它也不会返回非数字值。


2
我们拥有完整的jQuery库 - 否则我上面解释的$场景将无法工作。我的问题是关于为什么$可以工作,但即使完整的jQuery可用,angular.element也不能工作。 - irascian
1
你是在angular.js之前还是之后引入jQuery?应该在angular之前引入。如果有可用的jQuery,则id选择器可以正常工作:http://jsbin.com/ohumiy/1/edit - garst
1
他们正在另一个指令中工作(即使用angular.element通过id访问某些内容)。问题似乎在于该控件附加到该元素的方式,尽管我不明白为什么同样的jQuery等效物直接使用应该可以工作。直接使用jQuery时,我的指令必须有一个闭合标签-自闭合标签不起作用(没有错误,只有一个空屏幕),这使我怀疑该控件。 - irascian
2
在我的第二点中,还有一些类似于view$value的东西,可以用来检索元素的内容,但是在输入类型为number的情况下,如果用户输入了“--”,则为空。我想要一个类似于$rawInputValue的属性,以查看Angular介入之前的内容,因为“--”仍然存在于UI中,即使在我的指令中无法以编程方式访问它。 - irascian

29
var target = document.getElementById('appBusyIndicator');

等于

var target = $document[0].getElementById('appBusyIndicator');

1
使用第二个示例更好,以确保您的依赖项是明确的并且可以进行模拟,对吧? - Luke

18

如果有人使用 gulp,当我们使用 document.getElementById() 时会出现错误,并建议使用 $document.getElementById(),但这样并不起作用。

使用 -

$document[0].getElementById('id')

为什么Angular在这里注入一个数组? - Peter

17
您可以使用$document(需要注入$document)来访问元素。
var target = $document('#appBusyIndicator');
var target = $document('appBusyIndicator');

或者使用Angular元素,可以通过以下方式访问指定的元素:

var targets = angular.element(document).find('div'); //array of all div
var targets = angular.element(document).find('p');
var target = angular.element(document).find('#appBusyIndicator');

1
find() - 仅限于按标签名称查找 - RJV
angular.element(document).find('.someClass'); 运行得非常好。 - Nishant Baranwal
@NishantBaranwal 这是因为您已经加载了jQuery,否则它将使用不支持该功能的jQuery Lite。 - Kerry Johnson

17

我认为这不是使用Angular的正确方式。如果框架方法不存在,不要创建它!这意味着该框架(在这里是Angular)不是以这种方式工作的。

使用Angular时,不应像jQuery那样操纵DOM,而应使用Angular助手,例如

<div ng-show="isLoading" class="loader"></div>

或者创建自己的指令(自己的DOM组件),以便完全控制它。

顺便说一句,你可以在这里查看http://caniuse.com/#search=queryselector,querySelector 得到了很好的支持,因此可以安全地使用。


2
你说得完全正确。在我认为需要手动操作DOM的90%以上情况下,最终我都重构了代码以消除这种需求,最终代码更加清晰简洁。 - Stephen Chung

10

你应该在控制器中注入 $document,并使用它代替原始的 document 对象。

var myElement = angular.element($document[0].querySelector('#MyID'))

如果您不需要使用jquery样式的元素包装,$document[0].querySelector('#MyID') 将会给您返回DOM对象。


1
你可以直接引用window.document,而不必使用Angular的$document服务开销。 - MatBee
5
当然可以,如果您在单元测试中不担心模拟$document。 - Adamy
对我来说不是很清楚:我收到了这个警告:应该使用$document服务而不是默认的document对象,但这意味着什么?有相关文档吗?谢谢! - realnot
@realnot,你可以从JavaScript的任何地方访问HTML DOM文档对象。https://www.w3schools.com/jsref/dom_obj_document.asp - Adamy

6
这对我很有效。
angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});

3
如果我尝试使用没有 JQuery 的代码时出现“element is undefined”的错误,同时出现“angular.element.find不是一个函数”的错误。 - landed
3
请勿这样做,使用其他示例之一。这个示例完全没有使用哈希表查找优化。 - MatBee

4
改进kaiser的答案:
var myEl = $document.find('#some-id');

不要忘记将$document注入到你的指令中。

21
如同在 element.find 的 Angular 文档中所提到的:find() - 只能通过标签名进行查找,它不能用于查找 id。此外,您还多了一个多余的闭合括号 ) - paaat

3
也许我来晚了,但这个方法是有效的:
var target = angular.element(appBusyIndicator);

请注意,这里没有appBusyIndicator,只有普通的ID值。

背后发生了什么:(假设它应用在一个

元素上) (摘自angular.js第2769行及其之后...)

/////////////////////////////////////////////
function JQLite(element) {     //element = div#appBusyIndicator
  if (element instanceof JQLite) {
    return element;
  }

  var argIsString;

  if (isString(element)) {
    element = trim(element);
    argIsString = true;
  }
  if (!(this instanceof JQLite)) {
    if (argIsString && element.charAt(0) != '<') {
      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
    }
    return new JQLite(element);
  }

默认情况下,如果页面上没有jQuery,则会使用jqLite。该参数在内部被理解为一个id,并返回相应的jQuery对象。


我现在尝试使用Angular 1.5.8。一个普通的ID返回空结果。angular.element("#myId")运行良好。 - Gosha U.
也许你的页面上有jQuery? - Vishal Anand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接