سلوك عرض غير متناسق لقائمة التشغيل السريع في MOSS 2007

StackOverflow https://stackoverflow.com/questions/94154

  •  01-07-2019
  •  | 
  •  

سؤال

أحاول تكوين قائمة التشغيل السريع لعرض الأسلاف والعقد التابعة للعقدة المحددة حاليًا فقط.تحتاج القائمة أيضًا إلى عرض كافة العناصر التابعة للعقدة الجذرية.ببساطة أكثر:

نظرا لخريطة الموقع:

RootSite

---الموقع الفرعي1 = تم تعيين التنقل على "عرض الموقع الحالي وعناصر التنقل الموجودة أسفل الموقع الحالي وإخوة الموقع الحالي"

-----عنوان 1 = تم ضبط التنقل على "عرض نفس عناصر التنقل مثل الموقع الأصلي"

-------صفحة 1 = تم ضبط التنقل على "عرض نفس عناصر التنقل مثل الموقع الأصلي"

-------الصفحة2 = تم ضبط التنقل على "عرض نفس عناصر التنقل مثل الموقع الأصلي"

-----العنوان2 = تم ضبط التنقل على "عرض نفس عناصر التنقل مثل الموقع الأصلي"

---الموقع الفرعي2 = تم تعيين التنقل على "عرض الموقع الحالي وعناصر التنقل الموجودة أسفل الموقع الحالي وإخوة الموقع الحالي"

-----عنوان 1 = تم ضبط التنقل على "عرض نفس عناصر التنقل مثل الموقع الأصلي"

تكوين SiteMapProvider:

<PublishingNavigation:PortalSiteMapDataSource ID="SiteMapDS" Runat="server"
SiteMapProvider="CurrentNavSiteMapProvider" EnableViewState="true"
StartFromCurrentNode="true" ShowStartingNode="false"/>

السلوك المتوقع والفعلي لقائمة التشغيل السريع المعروضة في SubSite1 هو:

---الموقع الفرعي1

-----عنوان 1

-------صفحة 1

-------الصفحة 2

-----العنوان2

---الموقع الفرعي2

السلوك المتوقع للقائمة بعد الانتقال إلى Heading1 في SubSite2:

---الموقع الفرعي1

---الموقع الفرعي2

-----عنوان 1

ما أراه بالفعل بعد الانتقال إلى العنوان 1 في SubSite2:

---الموقع الفرعي1

-----عنوان 1

-------صفحة 1

-------الصفحة 2

-----العنوان2

---الموقع الفرعي2

-----عنوان 1

هذا لا يتطابق مع ما أتوقع أن أرى ما إذا كنت قمت بتعيين ملاحة Heading1 "لعرض نفس عناصر التنقل مثل الموقع الأصل" ، وتم تعيين subsite2 على "عرض الموقع الحالي وعناصر التنقل أسفل الموقع الحالي والموقع الحالي إخوة".أتوقع أن يرث Heading1 عنصر التنقل من subsite2 مع العناصر subsite1 المنهارة من العرض.لقد لعبت أيضًا مع مختلف القطع ...الصفات دون نجاح.أي مساعدة سوف تكون محل تقدير كبير!

هل كانت مفيدة؟

المحلول

لقد اتبعت إرشادات @Nat في أجزاء ويب Sharepoint العالمية الغامضة لتحقيق السلوك الذي وصفته أعلاه.كان نهجي هو طرح نسختي الخاصة من جزء ويب MossMenu التي أصدرتها Microsoft من خلال مدونة فريق ECM.يعتمد هذا الرمز على عنصر التحكم AspMenu الأصلي.لقد استخدمت عنصر التحكم هذا "لاعتراض" SiteMapDataSource الأصلي الذي تم إدخاله من خلال سمة DataSourceId في العلامات وإنشاء مصدر بيانات XML جديد لعرض السلوك المطلوب.لقد قمت بتضمين كود المصدر النهائي في نهاية هذه الإجابة اللفظية.فيما يلي البتات من ترميز الصفحة الرئيسية:

<%@ Register TagPrefix="myCustom" Namespace="YourCompany.CustomWebParts"
   Assembly="YourCompany.CustomWebParts, Version=1.0.0.0, Culture=neutral,
   PublicKeyToken=9f4da00116c38ec5" %>

...

<myCustom:MossMenu ID="CurrentNav" runat="server" datasourceID="SiteMapDS"
    orientation="Vertical" UseCompactMenus="true" StaticDisplayLevels="6"
    MaximumDynamicDisplayLevels="0" StaticSubMenuIndent="5" ItemWrap="false"
    AccessKey="3" CssClass="leftNav"
    SkipLinkText="<%$Resources:cms,masterpages_skiplinktext%>">
    <LevelMenuItemStyles>
        <asp:MenuItemStyle CssClass="Nav" />
        <asp:MenuItemStyle CssClass="SecNav" />
    </LevelMenuItemStyles>
    <StaticHoverStyle CssClass="leftNavHover"/>
    <StaticSelectedStyle CssClass="leftNavSelected"/>
    <DynamicMenuStyle CssClass="leftNavFlyOuts" />
    <DynamicMenuItemStyle CssClass="leftNavFlyOutsItem"/>
    <DynamicHoverStyle CssClass="leftNavFlyOutsHover"/>
</myCustom:MossMenu>

<PublishingNavigation:PortalSiteMapDataSource ID="SiteMapDS" Runat="server"
     SiteMapProvider="CurrentNavSiteMapProvider" EnableViewState="true"
     StartFromCurrentNode="true" ShowStartingNode="false"/>

...

لقد اتبعت الإرشادات الممتازة خطوة بخطوة لإنشاء جزء الويب المخصص الخاص بي في قسم التعليقات في جزء ويب MossMenu في "الأربعاء 19 سبتمبر 2007 الساعة 7:20 صباحًا بقلم رويل".أثناء بحثي على Google، وجدت أيضًا شيئًا لتكوين موقع Sharepoint لعرض الاستثناءات بنفس الطريقة الرائعة التي يفعلها ASP.NET عن طريق إجراء تغييرات على web.config هنا.

قررت أن أطلق على سلوكي المخصص اسم "القائمة المدمجة" لذلك قمت بإنشاء خاصية UseCompactMenus في عنصر التحكم.إذا لم تقم بتعيين هذه السمة في العلامات على true، فسوف يتصرف عنصر التحكم بشكل مماثل لعنصر تحكم AspMenu.

تطبيقي يجعل المستخدم يبدأ دائمًا من الصفحة الرئيسية في جذر خريطة الموقع.يمكنني جعل عنصر التحكم المخصص يخزن خريطة الموقع الأولية (الكاملة) عند عرض الصفحة الجذرية.يتم تخزين هذا في سلسلة ثابتة لاستخدامها في سلوك التخصيص.إذا لم يتبع تطبيقك هذا الافتراض، فلن يعمل عنصر التحكم كما هو متوقع.

في صفحة التطبيق الأولية، يتم عرض الصفحات الفرعية المباشرة إلى الصفحة الجذرية فقط في القائمة.سيؤدي النقر فوق عقد القائمة هذه إلى فتح جميع العقد الفرعية الموجودة تحتها ولكنه يبقي العقد الشقيقة "مغلقة".إذا قمت بالنقر فوق إحدى العقد الشقيقة الأخرى، فإنها تنهار العقدة الحالية وتفتح العقدة المحددة حديثًا.هذا كل شيء، استمتع!!

using System;
using System.Text;
using System.ComponentModel;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Xml;
using System.Xml.Serialization;

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Security;

namespace YourCompany.CustomWebParts
{
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    [Designer(typeof(MossMenuDesigner))]
    [ToolboxData("<{0}:MossMenu runat=\"server\" />")]
    public class MossMenu : System.Web.UI.WebControls.Menu
    {
        private string idPrefix;

        // a url->menuItem dictionary
        private Dictionary<string, System.Web.UI.WebControls.MenuItem> menuItemDictionary =
            new Dictionary<string, System.Web.UI.WebControls.MenuItem>(StringComparer.OrdinalIgnoreCase);

        private bool customSelectionEnabled = true;
        private bool selectStaticItemsOnly = true;

        private bool performTargetBinding = true;

        //** Variables used for compact menu behavior **//
        private bool useCompactMenus = false;
        private static bool showStartingNode;
        private static string originalSiteMap;

        /// <summary>
        /// Controls whether or not the control performs compacting of the site map to display only ancestor and child nodes of the selected and first level root childern.
        /// </summary>
        [Category("Behavior")]
        public bool UseCompactMenus
        {
            get
            {
                return this.useCompactMenus;
            }
            set
            {
                this.useCompactMenus = value;
            }
        }

        /// <summary>
        /// Controls whether or not the control performs custom selection/highlighting.
        /// </summary>
        [Category("Behavior")]
        public bool CustomSelectionEnabled
        {
            get
            {
                return this.customSelectionEnabled;
            }
            set
            {
                this.customSelectionEnabled = value;
            }
        }

        /// <summary>
        /// Controls whether only static items may be selected or if
        /// dynamic (fly-out) items may be selected too.
        /// </summary>
        [Category("Behavior")]
        public bool SelectStaticItemsOnly
        {
            get
            {
                return this.selectStaticItemsOnly;
            }
            set
            {
                this.selectStaticItemsOnly = value;
            }
        }

        /// <summary>
        /// Controls whether or not to bind the Target property of any menu
        /// items to the Target property in the SiteMapNode's Attributes
        /// collection.
        /// </summary>
        [Category("Behavior")]
        public bool PerformTargetBinding
        {
            get
            {
                return this.performTargetBinding;
            }
            set
            {
                this.performTargetBinding = value;
            }
        }

        /// <summary>
        /// Gets the ClientID of this control. 
        /// </summary>
        public override string ClientID
        {
            [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
            get
            {
                if (this.idPrefix == null)
                {
                    this.idPrefix = SPUtility.GetNewIdPrefix(this.Context);
                }

                return SPUtility.GetShortId(this.idPrefix, this);
            }
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void OnMenuItemDataBound(MenuEventArgs e)
        {
            base.OnMenuItemDataBound(e);

            if (this.customSelectionEnabled)
            {
                // store in the url->item dictionary
                this.menuItemDictionary[e.Item.NavigateUrl] = e.Item;
            }

            if (this.performTargetBinding)
            {
                // try to bind to the Target property if the data item is a SiteMapNode
                SiteMapNode smn = e.Item.DataItem as SiteMapNode;
                if (smn != null)
                {
                    string target = smn["Target"];
                    if (!string.IsNullOrEmpty(target))
                    {
                        e.Item.Target = target;
                    }
                }
            }
        }

        /// <id guid="08e034e7-5872-4a31-a771-84cac1dcd53d" />
        /// <owner alias="MarkWal">
        /// </owner>
        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void OnPreRender(System.EventArgs e)
        {

            SiteMapDataSource dataSource = this.GetDataSource() as SiteMapDataSource;
            SiteMapProvider provider = (dataSource != null) ? dataSource.Provider : null;

            if (useCompactMenus && dataSource != null && provider != null)
            {
                showStartingNode = dataSource.ShowStartingNode;

                SiteMapNodeCollection rootChildNodes = provider.RootNode.ChildNodes;

                if (provider.CurrentNode.Equals(provider.RootNode))
                {
                    //** Store original site map for future use in compacting menus **//
                    if (originalSiteMap == null)
                    {
                        //Store original SiteMapXML for future adjustments:
                        XmlDocument newSiteMapDoc = new XmlDocument();
                        newSiteMapDoc.LoadXml("<?xml version='1.0' ?>"
                            + "<siteMapNode title='" + provider.RootNode.Title
                            + "' url='" + provider.RootNode.Url
                            + "' />");

                        foreach (SiteMapNode node in rootChildNodes)
                        {
                            XmlNode newNode = GetXmlSiteMapNode(newSiteMapDoc.DocumentElement, node);

                            newSiteMapDoc.DocumentElement.AppendChild(newNode);

                            //Create XML for all the child nodes for selected menu item:
                            NavigateSiteMap(newNode, node);
                        }

                        originalSiteMap = newSiteMapDoc.OuterXml;
                    }

                    //This is set to only display the child nodes of the root node on first view:
                    this.StaticDisplayLevels = 1;
                }
                else
                {
                    //
                    //Adjust site map for this page
                    //
                    XmlDocument newSiteMapDoc = InitializeNewSiteMapXml(provider, rootChildNodes);

                    //Clear the current default site map:
                    this.DataSourceID = null;

                    //Create the new site map data source
                    XmlDataSource newSiteMap = new XmlDataSource();
                    newSiteMap.ID = "XmlDataSource1";
                    newSiteMap.EnableCaching = false; //Required to prevent redisplay of the previous menu

                    //Add bindings for dynamic site map:
                    MenuItemBindingCollection bindings = this.DataBindings;
                    bindings.Clear();

                    MenuItemBinding binding = new MenuItemBinding();
                    binding.DataMember = "siteMapNode";
                    binding.TextField = "title";
                    binding.Text = "title";
                    binding.NavigateUrlField = "url";
                    binding.NavigateUrl = "url";
                    binding.ValueField = "url";
                    binding.Value = "url";

                    bindings.Add(binding);

                    //Bind menu to new site map:
                    this.DataSource = newSiteMap;

                    //Assign the newly created dynamic site map:
                    ((XmlDataSource)this.DataSource).Data = newSiteMapDoc.OuterXml;

                    /** this expression removes the root if initialized: **/
                    if (!showStartingNode)
                        ((XmlDataSource)this.DataSource).XPath = "/siteMapNode/siteMapNode";

                    /** Re-initialize menu data source with new site map: **/
                    this.DataBind();

                    /** Find depth of current node: **/
                    int depth = 0;
                    SiteMapNode currNode = provider.CurrentNode;
                    do
                    {
                        depth++;
                        currNode = currNode.ParentNode;
                    }
                    while (currNode != null);

                    //Set the StaticDisplayLevels to match the current depth:
                    if (depth >= this.StaticDisplayLevels)
                        this.StaticDisplayLevels = depth;
                }
            }

            base.OnPreRender(e);

            // output some script to override the default menu flyout behaviour; this helps to avoid
            // intermittent "Operation Aborted" errors
            Page.ClientScript.RegisterStartupScript(
                typeof(MossMenu),
                "overrideMenu_HoverStatic",
                "if (typeof(overrideMenu_HoverStatic) == 'function' && typeof(Menu_HoverStatic) == 'function')\n" +
                "{\n" +
                    "_spBodyOnLoadFunctionNames.push('enableFlyoutsAfterDelay');\n" +
                    "Menu_HoverStatic = overrideMenu_HoverStatic;\n" +
                "}\n",
                true);

            // output some script to avoid a known issue with SSL Termination and the ASP.NET
            // Menu implementation. http://support.microsoft.com/?id=910444
            Page.ClientScript.RegisterStartupScript(
                typeof(MossMenu),
                "MenuHttpsWorkaround_" + this.ClientID,
                this.ClientID + "_Data.iframeUrl='/_layouts/images/blank.gif';",
                true);

            // adjust the fly-out indicator arrow direction for locale if not already set
            if (this.Orientation == System.Web.UI.WebControls.Orientation.Vertical &&
                ((string.IsNullOrEmpty(this.StaticPopOutImageUrl) && this.StaticEnableDefaultPopOutImage) ||
                    (string.IsNullOrEmpty(this.DynamicPopOutImageUrl) && this.DynamicEnableDefaultPopOutImage)))
            {
                SPWeb currentWeb = SPContext.Current.Web;
                if (currentWeb != null)
                {
                    uint localeId = currentWeb.Language;

                    bool isBidiWeb = SPUtility.IsRightToLeft(currentWeb, currentWeb.Language);

                    string arrowUrl = "/_layouts/images/" + (isBidiWeb ? "largearrowleft.gif" : "largearrowright.gif");

                    if (string.IsNullOrEmpty(this.StaticPopOutImageUrl) && this.StaticEnableDefaultPopOutImage)
                    {
                        this.StaticPopOutImageUrl = arrowUrl;
                    }
                    if (string.IsNullOrEmpty(this.DynamicPopOutImageUrl) && this.DynamicEnableDefaultPopOutImage)
                    {
                        this.DynamicPopOutImageUrl = arrowUrl;
                    }
                }
            }

            if (provider == null)
            {
                // if we're not attached to a SiteMapDataSource we'll just leave everything alone
                return;
            }
            else if (this.customSelectionEnabled)
            {
                MenuItem selectedMenuItem = this.SelectedItem;
                SiteMapNode currentNode = provider.CurrentNode;

                // if no menu item is presently selected, we need to work our way up from the current 
                // node until we can find a node in the menu item dictionary
                while (selectedMenuItem == null && currentNode != null)
                {
                    this.menuItemDictionary.TryGetValue(currentNode.Url, out selectedMenuItem);

                    currentNode = currentNode.ParentNode;
                }

                if (this.selectStaticItemsOnly)
                {
                    // only static items may be selected, keep moving up until we find an item
                    // that falls within the static range
                    while (selectedMenuItem != null && selectedMenuItem.Depth >= this.StaticDisplayLevels)
                    {
                        selectedMenuItem = selectedMenuItem.Parent;
                    }

                    // if we found an item to select, go ahead and select (highlight) it
                    if (selectedMenuItem != null && selectedMenuItem.Selectable)
                    {
                        selectedMenuItem.Selected = true;
                    }
                }
            }
        }

        private XmlDocument InitializeNewSiteMapXml(SiteMapProvider provider, SiteMapNodeCollection rootChildNodes)
        {
            /** Find the level 1 ancestor node of the current node: **/
            SiteMapNode levelOneAncestorOfSelectedNode = null;
            SiteMapNode currNode = provider.CurrentNode;
            do
            {
                levelOneAncestorOfSelectedNode = (currNode.ParentNode == null ? levelOneAncestorOfSelectedNode : currNode);
                currNode = currNode.ParentNode;
            }
            while (currNode != null);

            /** Initialize base SiteMapXML **/
            XmlDocument newSiteMapDoc = new XmlDocument();
            newSiteMapDoc.LoadXml(originalSiteMap);

            /** Prune out the childern nodes that shouldn't display: **/
            currNode = provider.CurrentNode;
            do
            {
                if (currNode.ParentNode != null)
                {
                    SiteMapNodeCollection currNodeSiblings = currNode.ParentNode.ChildNodes;
                    foreach (SiteMapNode siblingNode in currNodeSiblings)
                    {
                        if (siblingNode.HasChildNodes)
                        {
                            if (provider.CurrentNode.Equals(siblingNode))
                            {
                                //Remove all the childerns child nodes from display:
                                SiteMapNodeCollection currNodesChildren = siblingNode.ChildNodes;
                                foreach (SiteMapNode childNode in currNodesChildren)
                                {
                                    XmlNode currentXmNode = GetCurrentXmlNode(newSiteMapDoc, childNode);

                                    DeleteChildNodes(currentXmNode);
                                }
                            }
                            else if (!provider.CurrentNode.IsDescendantOf(siblingNode)
                                && !levelOneAncestorOfSelectedNode.Equals(siblingNode))
                            {
                                XmlNode currentXmNode = GetCurrentXmlNode(newSiteMapDoc, siblingNode);

                                DeleteChildNodes(currentXmNode);
                            }
                        }
                    }
                }

                currNode = currNode.ParentNode;
            }
            while (currNode != null);

            return newSiteMapDoc;
        }

        private XmlNode GetCurrentXmlNode(XmlDocument newSiteMapDoc, SiteMapNode node)
        {
            //Find this node in the original site map:
            XmlNode currentXmNode = newSiteMapDoc.DocumentElement.SelectSingleNode(
                "//siteMapNode[@url='"
                + node.Url
                + "']");
            return currentXmNode;
        }

        private void DeleteChildNodes(XmlNode currentXmNode)
        {
            if (currentXmNode != null && currentXmNode.HasChildNodes)
            {
                //Remove child nodes:
                XmlNodeList xmlNodes = currentXmNode.ChildNodes;
                int lastNodeIndex = xmlNodes.Count - 1;
                for (int i = lastNodeIndex; i >= 0; i--)
                {
                    currentXmNode.RemoveChild(xmlNodes[i]);
                }
            }
        }
        private XmlNode GetXmlSiteMapNode(XmlNode currentDocumentNode, SiteMapNode currentNode)
        {
            XmlElement newNode = currentDocumentNode.OwnerDocument.CreateElement("siteMapNode");

            XmlAttribute newAttr = currentDocumentNode.OwnerDocument.CreateAttribute("title");
            newAttr.InnerText = currentNode.Title;
            newNode.Attributes.Append(newAttr);

            newAttr = currentDocumentNode.OwnerDocument.CreateAttribute("url");
            newAttr.InnerText = currentNode.Url;
            newNode.Attributes.Append(newAttr);

            return newNode;
        }

        private void NavigateSiteMap(XmlNode currentDocumentNode, SiteMapNode currentNode)
        {
            foreach (SiteMapNode node in currentNode.ChildNodes)
            {
                //Add this node to structure:
                XmlNode newNode = GetXmlSiteMapNode(currentDocumentNode, node);
                currentDocumentNode.AppendChild(newNode);

                if (node.HasChildNodes)
                {
                    //Make a recursive call to add any child nodes:
                    NavigateSiteMap(newNode, node);
                }
            }
        }
    }

    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2117:AptcaTypesShouldOnlyExtendAptcaBaseTypes")]
    public sealed class MossMenuDesigner : MenuDesigner
    {
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        protected override void DataBind(BaseDataBoundControl dataBoundControl)
        {
            try
            {
                dataBoundControl.DataBind();
            }
            catch
            {
                base.DataBind(dataBoundControl);
            }
        }

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public override string GetDesignTimeHtml()
        {
            System.Web.UI.WebControls.Menu menu = (System.Web.UI.WebControls.Menu)ViewControl;
            int oldDisplayLevels = menu.MaximumDynamicDisplayLevels;
            string designTimeHtml = string.Empty;

            try
            {
                menu.MaximumDynamicDisplayLevels = 0;

                // ASP.NET MenuDesigner has some dynamic/static item trick in design time
                // to show dynamic item in design time. We only want to show preview without
                // dynamic menu items.
                designTimeHtml = base.GetDesignTimeHtml();
            }
            catch (Exception e)
            {
                designTimeHtml = GetErrorDesignTimeHtml(e);
            }
            finally
            {
                menu.MaximumDynamicDisplayLevels = oldDisplayLevels;
            }

            return designTimeHtml;
        }
    }
}

نصائح أخرى

أنا شخصياً لا أحب لغة HTML التي توفرها القائمة الافتراضية (التخطيط المستند إلى الجدول).لحسن الحظ، قام فريق SharePoint بإصدار الكود الخاص بذلك يتحكم.

ما فعلناه هو تضمين هذا الرمز في المشروع وتجاوزنا طريقة العرض للقيام بكل ما نريد.يمنحك هذا المرونة لتحديد العلاقة الدقيقة بين الوالدين التي يجب عرضها بالإضافة إلى تعيين الأنماط على أي div تقوم بإنشائه.

على الجانب السلبي، أنت الآن تقوم بالترميز، وليس التكوين، ويجب إجراء تغيير على الصفحة الرئيسية التي تستخدمها لاستخدام عنصر التحكم.

يستحق ذلك في رأيي.يعد هذا الآن تغييرًا قياسيًا نقوم به لأي موقع.

كان النهج الذي استخدمناه لتحقيق التأثير الذي تبحث عنه هو استخدام محولات التحكم الصديقة لـ CSS.تقوم المحولات بتغيير HTML الذي يتم عرضه دون تغيير عناصر التحكم التي استخدمتها في صفحاتك.قد تحتاج إلى تعديل محول القائمة قليلاً للحصول على التصميم الذي تريده.لم يستغرق الأمر سوى بضعة أسطر من التعليمات البرمجية بالنسبة لنا.بمجرد الانتهاء من ذلك، يمكنك استخدام CSS للحصول على السلوك الذي تصفه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top