简体   繁体   English

JavaScript function 操纵 CSS 自定义属性不适用于 $(window).resize()

[英]JavaScript function to manipulate CSS custom properties not working on $(window).resize()

I have a fixed header with :target in-page anchors, and need to adjust the property values dynamically via JavaScript or JQuery so as to maintain the relevant :target 's position directly under the header when the window is resized, while adapting to the changes in both the previous section's.container height and the .header_container height that occur with resizing. I have a fixed header with :target in-page anchors, and need to adjust the property values dynamically via JavaScript or JQuery so as to maintain the relevant :target 's position directly under the header when the window is resized, while adapting to the上一节的 .container 高度和.header_container高度在调整大小时都会发生变化。

The simplest solution seems to be a ::before pseudo-element for the :target pseudo-class, and to then utilize CSS custom properties to dynamically modify the style properties.最简单的解决方案似乎是:target伪类的::before伪元素,然后利用 CSS 自定义属性来动态修改样式属性。

I have no trouble correctly positioning the :target with the below function when the page is loaded (or reloaded), or correctly position the first :target on $(window).resize() , however it's failing to do the same for the remaining targets on $(window).resize() .当页面加载(或重新加载)时,我没有问题正确定位:target与下面的 function ,或者正确定位 position 上的第一个:target$(window).resize() ,但是它没有对其余的做同样的事情$(window).resize()上的目标。

Fiddles小提琴

Simplified Code: https://jsfiddle.net/chayanyc/g6p3549s/简化代码: https://jsfiddle.net/chayanyc/g6p3549s/

Responsive Design (Simplified): https://jsfiddle.net/chayanyc/wuk92dns/响应式设计(简化): https://jsfiddle.net/chayanyc/wuk92dns/

Code Snippets代码片段

CSS: CSS:

.header_container {height: 98px; margin: 0; padding: 0; position: fixed; top: 0; display: block; z-index: 100;}
.main {margin-top: 98px; width: 100%;}
:target::before {height: var(--target_position1); margin-top: var(--target_position2); content: ""; display: block; visibility: hidden;}

JavaScript: JavaScript:

var headerHeight;

function setTarget() {
    headerHeight = document.getElementById('header').offsetHeight;
    headerHeight1 = headerHeight + "px";       
    document.documentElement.style.setProperty('--target_position1', headerHeight1);
    document.documentElement.style.setProperty('--target_position2', '-' + headerHeight1);
}

$(window).resize(function () {
    setTarget();
});

$(document).ready(function () {
    setTarget();
});

There is no complete solution to this Problem, because you want the target element stay on place on document resize, but if the user do a scroll on his page, it is not possible to know where staying on the same first word of the first line on display.这个问题没有完整的解决方案,因为您希望目标元素在文档调整大小时保持原位,但如果用户在他的页面上滚动,则无法知道第一行的同一个单词上的位置展示中。 So here, i just replace on the same target on top when user resize his document, even if he had done a scroll just before.所以在这里,当用户调整他的文档大小时,我只是在顶部的同一个目标上替换,即使他之前已经完成了滚动。

no need of this CSS part (remove it)不需要这个 CSS 部分(删除它)

:target::before {margin: 0; content: ""; dis.....

and change your jQuery to:并将您的 jQuery 更改为:

$(document).ready(function () {

  // global info for menu -> target elememt
  var InfoTarget = { ID: null, tempo:300 }

  $('a').click(function(evt){
    InfoTarget.ID = $(this).attr('href') // possible target elm

    // check if InfoTarget.ID exist on page
    let nbElements = 0
    try        { nbElements = $(InfoTarget.ID).length  }
    catch(err) { nbElements = 0 }

    if ( nbElements != 1 ) {
      InfoTarget.ID = null  // not target element found
    }
    else {
      evt.preventDefault()   // disable auto scroll to target element  

      $('html').animate({
          scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
      }, InfoTarget.tempo  );
    }
  });

  $(window).resize(function (){
    if (InfoTarget.ID) {  // if InfoTarget.ID exist <=> InfoTarget.ID != null
      $('html').animate({
        scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
      }, InfoTarget.tempo);
    }
  });

});

My code speaks for itself, but here is a complete explanation:我的代码不言自明,但这里有一个完整的解释:

the principle is simple: the function target css activates on a click on a link <a href="#..."> to trigger a scroll of the page towards the element having for id = to that contained in the initial href.原理很简单:function 目标 css 在单击链接<a href="#...">时激活,以触发页面滚动到具有 for id = 初始 href 中包含的元素的元素。

therefore this code intercepts any click on a link on the page and must first determine whether it is a link to an anchor or not.因此,此代码拦截对页面上链接的任何点击,并且必须首先确定它是否是指向锚点的链接。

To determine if this is a link to an anchor on the page, it simply tests whether an element of the page has this value as this ID , ( // check if InfoTarget.ID exists on page ).要确定这是否是指向页面上锚点的链接,它只需测试页面的元素是否将此值作为此ID ,( // check if InfoTarget.ID exists on page )。
As this kind of test can also generate an error, this test is placed in a try / catch .由于这种测试也会产生错误,所以这个测试被放在了try / catch中。

If the result is indeed an anchor, then the action of the click is canceled, (with evt.preventDefault() ) which prevents the browser from triggering its automatic scroll to the link;如果结果确实是一个锚点,则取消单击操作(使用evt.preventDefault() ),这会阻止浏览器触发其自动滚动到链接;

the reference link is kept in an object variable (global)参考链接保存在 object 变量(全局)中

var InfoTarget = {ID: null, tempo: 300} var InfoTarget = {ID:null,速度:300}

seen on: InfoTarget.ID = $(this).attr('href') // possible target elm见于: InfoTarget.ID = $(this).attr('href') // possible target elm

the rest is simple, you have to scroll down to the anchor. rest 很简单,您必须向下滚动到锚点。 Depending on the width of the page and the previous elements, browsers continuously recalculate the position of each tag present on a page and jQuery can be retrieved this offset position by $(element).offset().Top根据页面的宽度和前面的元素,浏览器会不断地重新计算页面上每个标签的 position,并且 jQuery 可以通过$(element).offset().Top

as there is a menu bar on your page that masks the top of the page, you must deduct its height from the position in scroll (= $ ('# header'). outerHeight (true))由于您的页面上有一个菜单栏遮住了页面的顶部,因此您必须从滚动中的 position 中减去其高度(= $ ('# header').outerHeight (true))
a scroll = 0 will force a move to the top of the page scroll = 0将强制移动到页面顶部
a scroll = $(element).offset().top places the element at the top of the page to which we must deduct the height of the #header a scroll = $(element).offset().topelement放置在我们必须减去#header高度的页面顶部

the complete formula is完整的公式是

scrollTop: ( $(InfoTarget.ID).offset().top - $('#header').outerHeight(true) ) scrollTop: ( $(InfoTarget.ID).offset().top - $('#header').outerHeight(true) )

this command is placed in a jQuery.animate , for a visually smoother move, and uses the InfoTarget.tempo value as the duration for this animation.此命令放置在jQuery.animate中,以实现视觉上更平滑的移动,并使用InfoTarget.tempo值作为此 animation 的持续时间。

During a resize of the page, and to the extent that a link having a target has been previously clicked (therefore always active) then the same type of scroll is triggered.在调整页面大小的过程中,如果先前点击了具有目标的链接(因此始终处于活动状态),则会触发相同类型的滚动。

The different jQuery methods used are all explained in the jQuery doc (for example: https://api.jquery.com/outerHeight/ ) The different jQuery methods used are all explained in the jQuery doc (for example: https://api.jquery.com/outerHeight/ )

New Solution -- Lundi 14 oct 2019 / 01:00 (in the night of sunday / monday)新解决方案——Lundi 2019 年 10 月 14 日 / 01:00(周日/周一晚上)

this script must be placed after all the html elements of the body此脚本必须放在正文的所有 html 元素之后

// scroll to target upon window.location.hash 
$(window).on('hashchange', function() {
  $('.TargetMark').removeClass('TargetMark')
  $(window.location.hash).addClass('TargetMark')

  setTimeout( scrollTop2, 220 )  // scroll to target after browser auto scrolling conflit
})

function scrollTop2() {
  if ($('.TargetMark').length===1) { // if target exist
    $('html').animate({
        scrollTop: ($('.TargetMark').offset().top - $('#header').outerHeight(true))
    }, 100);
  }
}

In this version the target element is added a class (TargetMark) allowing to find it when window resize在这个版本中,目标元素添加了一个 class (TargetMark) 允许在 window 调整大小时找到它

ending part结束部分

$(document).ready(function () {
  //...
  // --------------------------->  no call to scrollTop();
  //...
});

$(window).resize(function () {
  //...
  scrollTop2();
  //...
});

about toggleMenu conflict:关于 toggleMenu 冲突:

function toggleMenu() {
  $('.navbar-toggle').on('click', function () {
    if ($("#js-menu").is(".expand")) {
      $("#js-menu").toggleClass("expand");
      $("#submenu").removeClass("active_sub").addClass("inactive_sub");
    } else {
      $("#js-menu").toggleClass("expand");
      $("#submenu").removeClass("inactive_sub").addClass("active_sub");
    }
    resetTarget();
    setTimeout( scrollTop2, 220 )  // scroll to target after browser auto scrolling conflit
  });
}


I spent a lot of my time on your question, I studied differents approaches and the different automatisms put at work by the navigators themselves and which is necessary to fight to get the result you'r looking for. 我花了很多时间在你的问题上,我研究了不同的方法和导航员自己在工作中的不同自动化,这对于争取得到你想要的结果是必要的。 I came to the conclusion that the problem first came from the architecture of your page. 我得出的结论是,问题首先来自您页面的架构。
The fact that the menu ("#header") covers the page ("#main") is a major flaw that prevents to have an effective JS code for your question. 菜单(“#header”)覆盖页面(“#main”)这一事实是一个主要缺陷,它会阻止为您的问题提供有效的 JS 代码。
The call on the hash of an anchor triggers one or more scrolls of the page, the resize of the page also entails a scroll calculation because by changing size on the screen, it also changes the size of the page. 对锚点的 hash 的调用触发页面的一次或多次滚动,页面的调整大小也需要滚动计算,因为通过改变屏幕上的大小,它也会改变页面的大小。 page (reducing the size of the screen by half makes the page size double), it is the same by changing the size of the font, it also changes the size in page. 页面(屏幕缩小一半,页面大小翻倍),同样通过改变字体大小,也改变了页面大小。
Whenever the page size changes, the browser must recalculate a lot of things and some of these mechanisms can trigger one or more scrolls. 每当页面大小发生变化时,浏览器必须重新计算很多东西,其中一些机制可以触发一个或多个滚动。
What you are asking here is to recalculate a page positioning according to an element of which we can not be certain that it is completely established because this process is executed in parallel with other processes of the browser which can change useful values. 你在这里问的是根据一个我们不能确定它是否完全建立的元素重新计算页面定位,因为这个过程与浏览器的其他进程并行执行,可以改变有用的值。
Plus the fact that some of the browser processes also work to scroll the page and that it can be the last done! 再加上一些浏览器进程也可以滚动页面并且它可以是最后完成的事实!
So the fact that there is an overlap between the menu and the page add more complexity and makes the possibility of a solution impossible. 因此,菜单和页面之间存在重叠的事实增加了复杂性,并使解决方案的可能性变得不可能。 Change your layout and 3/4 of your problem will be fixed. 改变你的布局,你的问题的 3/4 将得到修复。

I'm frankly discouraged by this project, my first solution worked, but you rejected it because your question was badly asked initially and did not mention navigation management.坦率地说,我对这个项目感到沮丧,我的第一个解决方案有效,但您拒绝了它,因为您的问题最初被问得很糟糕并且没有提到导航管理。 I took on myself and I deepened my analysis to propose another solution, there are only twenty lines of code each time, but believe it, they are the result of a few sleepless nights that I spent for you (I wrote it above, but obviously you do not care).我承担起自己,深入分析提出另一种解决方案,每次只有二十行代码,但相信它,它们是我为你度过的几个不眠之夜的结果(我写在上面,但显然你不关心)。

I think that your HTML page and your CSS are blocking any solution on this issue.我认为您的 HTML 页面和您的 CSS 阻止了有关此问题的任何解决方案。 Moreover, the whole could be written in a simpler and more efficient way.此外,可以以更简单和更有效的方式编写整体。 But that's another question and I prefer to spend my time on other topics here where I can growing my reputation points without striving on a time-consuming problem badly built.但这是另一个问题,我更愿意在这里花时间在其他主题上,在那里我可以增加我的声誉点,而无需努力解决一个耗时的问题。

Resize is firing, offset height is not changing.调整大小正在触发,偏移高度没有改变。 Setting the same value over and over again, yields no change.一遍又一遍地设置相同的值,不会产生任何变化。 You might check this:你可以检查这个:

see the value change查看价值变化

I used the logo for output:我使用了 output 的标志:

$('.logo').text(headerHeight + ' -' + i++);

You want to scroll down to the one div selected by target without having it to be overlapped by your nav?您想向下滚动到目标选择的一个 div 而不让它与您的导航重叠吗?

.. then extend the areas. ..然后扩展区域。 see here add positive margin-top and negative padding-top .请参阅此处添加正margin-top和负padding-top

.... to compensate for any nav size changes, use media queries to change your css vars. ....为了补偿任何导航大小的变化,使用媒体查询来改变你的 css 变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM