如何在 ASP.NET 中按类而不是 ID 选择元素?

Posted

技术标签:

【中文标题】如何在 ASP.NET 中按类而不是 ID 选择元素?【英文标题】:How to select an element by Class instead of ID in ASP.NET? 【发布时间】:2011-02-15 12:31:29 【问题描述】:

我在 aspx 页面上有一些分散的 <p> 元素,我使用这样的类将它们组合在一起 - <p class="instructions" runat="server">

在我的代码后面,使用 C# 我想隐藏这些元素,使用类似的东西 instructions.Visible = false;

但是我意识到如果我使用 ID,我只能在代码隐藏中执行此操作,但这会导致 html/CSS 选择器无效,因为您不能拥有多个 ID 名称相同的 ID...

如果不是按类,还有其他方法可以对控件进行分组吗?

编辑:我不能使用 javascript,所以选择必须在 C# codebehind/ASP.NET 中完成

【问题讨论】:

【参考方案1】:

事情很简单。在您的 ASPX 中:

<p class="instructions" runat="server" OnPreRender="Paragraph_PreRender">

在您的代码隐藏中:

protected void Paragraph_PreRender(object sender, EventArgs e)

  Control paragraph = (Control)sender;
  paragraph.Visible = !paragraph.CssClass.Contains("instructions");

代码隐藏将自动连接到类中的 PreRender 事件处理程序。这会将发送者转换为控件,并将其 Visibility 设置为依赖于 css 类。 您只需要调整标签,不需要大量代码遍历您的控件集合。

【讨论】:

这解决了我的问题。由于我是动态创建对象,因此我以这种方式分配函数= thumbnail.PreRender += funcThumbnail_PreRender;一切都完美无缺! 绝对是在后端显示/隐藏控件的最简单和最干净的方法。谢谢!【参考方案2】:

除了将所有控件分组到一个容器控件中之外,没有简单的方法可以在 ASP.NET 服务器端代码中找到给定某些属性的一组控件。

在客户端,您可以使用 jQuery 之类的东西来查找这些元素并隐藏它们:

$(".instructions").hide();

当页面完全加载时,我可能会这样做:

$(document).ready(function()  
   $(".instructions").hide(); 
);

在 Javascript 中隐藏元素的一个缺点是,如果有足够的数据,它可能需要一秒钟的时间并导致内容闪烁。另一个区别是隐藏内容客户端不会从 DOM 中删除它 - 内容只是隐藏在那里。在服务器端隐藏控件可防止其内容甚至被发送到 HTML。

在 C# 中做同样的事情有点困难——它需要递归地遍历控制树并在 Control 集合中寻找匹配的元素。这是一个足够常见的操作,实用函数很有用。 C# iterator syntax(收益回报)有助于清理:

// utility method to recursively find controls matching a predicate
IEnumerable<Control> FindRecursive( Control c, Func<Control,bool> predicate )

    if( predicate( c ) )
        yield return c;

    foreach( var child in c.Controls )
    
        if( predicate( c ) )
            yield return c;
    

    foreach( var child in c.Controls )
        foreach( var match in FindRecursive( c, predicate ) )
           yield return match;


// use the utility method to find matching controls...
FindRecursive( Page, c => (c is WebControl) && 
                          ((WebControl)c).CssClass == "instructions" );

现在隐藏控件相对容易:

foreach( WebControl c in FindRecursive( Page, c => (c is WebControl) && 
                           ((WebControl)c).CssClass == "instructions" ) )

    c.Visible = false;

【讨论】:

我喜欢这个主意。使方法成为扩展方法,然后你可以简单地去: Page.FindRecursive(c => (c is WebControl) && ((WebControl)c).CssClass == "instructions")).ForEach(c => ((WebControl )c).Visible = false); 这看起来很棒,C# 解决方案正是我想要的。 也许再看看我的回答。由于不需要遍历控件集合,它的代码更少,而且不那么昂贵。当然,您需要调整 aspx 中的

元素作为缺点。

这段代码会导致溢出异常,不应该使用。此外,它的缺陷在于它不遍历子控件(在每个 foreach 循环中,子控件都是未使用的)。 (见下面的黑猫回答) 鉴于这是公认的答案并且几乎是正确的,也许@LBushkin 可以更正它,这样就无需进一步阅读以获得几乎相同的东西。 (即它是否只需要将child 传递到嵌套的foreach 而不是传递c?)【参考方案3】:

我想回答第一个答案 - 我们使用递归来遍历所有控件。首先,我们不应该递归子项吗?我没有仔细看代码,发现我们一直在“c”而不是“child”上递归调用该方法。其次,我发现我的网页上的所有项目都不能转换为 WebControl - 只能转换为 HtmlGenericControl。

编辑后,我有这个:

    // utility method to recursively find controls matching a predicate
    IEnumerable<Control> FindRecursive( Control c, Func<Control,bool> predicate )
    
        if( predicate( c ) )
            yield return c;

        foreach (var child in c.Controls) 
            if (predicate((Control)child)) 
               yield return (Control)child;
            
        

        foreach( var child in c.Controls )
            foreach( var match in FindRecursive( (Control)child, predicate ) )
               yield return match;
    

    foreach (Control c in FindRecursive(Page, c => (c is HtmlGenericControl) &&
         ((HtmlGenericControl)c).Attributes["ishidden"] == "1"))
    
         c.Visible = false;
    

请注意,我不能使用“CssClass” - 我必须使用自己的属性 ('ishidden') 才能使其工作。

<div runat="server" ishidden="1"> ... </div>

我正在使用 ASP.NET 框架 2.0/3.0/3.5。

【讨论】:

【参考方案4】:

如果你包含 JQuery 核心,你所要做的就是在你的页面上注册这个脚本:

<script>
 $(document).ready(function() 
    $(".instructions").hide();
 );
</script>

它使用 JQuery 类选择器http://api.jquery.com/class-selector/

【讨论】:

我喜欢这个解决方案 - 我主要担心我不能使用 JavaScript,因为某些客户端启用了 JavaScript。【参考方案5】:

您可以创建一个递归函数来遍历页面控件数组,检查每个控件的 CssClass 属性并根据需要进行设置。

【讨论】:

这基本上是当您执行 getElementsByTagName 或使用 jquery 选择器按类选择时在客户端上发生的情况。不是遍历 DOM,而是遍历 asp.net 控制树。 我打算输入一个粗略的代码示例 - 但 LBushkin 发布的方式比我的要好!【参考方案6】:

&lt;asp:Panel&gt; 接近

如果它们连续放置在您的表单中,您可以将它们全部放在 an 中。这样您就可以轻松切换面板的 .Visible 属性来隐藏表单的块。

JavaScript 方法

您可以使用ClientScriptManager.RegisterStartupScript() 发出 JavaScript,然后使用 jQuery 按类隐藏。

http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx

【讨论】:

很好的解决方案,不幸的是我不能使用 JS 并且我的段落分散 - 这就是为什么我用类标记它们。【参考方案7】:

在 Sebastian P.R. Gingter 解决方案的基础上,这就是我所做的,尽管考虑到我必须使用基于 MS 的 WebControl 而不是选择更简单的 HTML 控件,但它仍然感觉有点像 hack。

在 C# 中:

protected void Paragraph_PreRender(object sender, EventArgs e) 

  WebControl paragraph = (WebControl)sender;
  paragraph.Visible = !paragraph.CssClass.Contains("instructions"); 
 

在 aspx 中:

 <asp:Label ID="Label1" CssClass="instructions" runat="server" Text="Label" onPreRender="Paragraph_PreRender"></asp:Label>

【讨论】:

【参考方案8】:

我已经测试了 blackcatweb 的解决方案。它返回重复项,所以我修复了它并为 WebControls 添加了方法。我的代码如下。按类设置 attrName="class" 搜索:

    /// <summary>
    /// Find controls
    /// </summary>
    /// <param name="c">Control</param>
    /// <param name="predicate">Function</param>
    /// <returns>Control's</returns>
    public static IEnumerable<Control> FindRecursive(Control c, Func<Control, bool> predicate)
    
        if (predicate(c))
        
            yield return c;
        

        foreach (Control child in c.Controls)
        
            foreach (Control founded in FindRecursive(child, predicate))
            
                yield return founded;
            
        
    

    /// <summary>
    /// Find WebControls by attr
    /// </summary>
    /// <param name="c">Control</param>
    /// <returns>WebControls</returns>
    public static IEnumerable<WebControl> FindWebControlsByAttr(Control baseControl, string attrName, string attrValue)
    
        foreach (WebControl c in FindRecursive(baseControl, c => (c is WebControl)
            && !string.IsNullOrEmpty(((WebControl)c).Attributes[attrName])
            && ((WebControl)c).Attributes[attrName].Contains(attrValue)))
        
            yield return c;
        
    

    /// <summary>
    /// Find HtmlGenericControls by attr
    /// </summary>
    /// <param name="c">Control</param>
    /// <returns>HtmlGenericControls</returns>
    public static IEnumerable<HtmlGenericControl> FindControlsByAttr(Control baseControl, string attrName, string attrValue)
    
        foreach (HtmlGenericControl c in FindRecursive(baseControl, c => (c is HtmlGenericControl)
            && !string.IsNullOrEmpty(((HtmlGenericControl)c).Attributes[attrName])
            && ((HtmlGenericControl)c).Attributes[attrName].Contains(attrValue)))
        
            yield return c;
        
    

【讨论】:

【参考方案9】:

您可以为此目的使用JQuery Class Name Selector。 另一种解决方案是保持相同的 ID,而不是使控件服务器端。在 JavaScript 中使用 document.getElementById 来获取控件,在您的情况下,您将获得一个数组,该数组将保存所有具有相同 ID 的控件。遍历这些控件并相应地设置它们的显示属性。

【讨论】:

以上是关于如何在 ASP.NET 中按类而不是 ID 选择元素?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 依赖注入单个聚合类而不是多个单独的构造函数注入

使用 WebDriver 在 codeceptJS 中按类获取元素

使用元类而不是工厂模式

在 ASP.NET RadioButtonList ListItem 上设置 CSS 类

javascript 在Tachein中按类添加交互

如何使用 jQuery 更改标签值,而不按类或 ID 选择?