Buộc DOM vẽ lại / làm mới trên Chrome / Mac


215

Thỉnh thoảng, Chrome sẽ hiển thị HTML / CSS hoàn toàn hợp lệ không chính xác hoặc hoàn toàn không. Đi sâu vào trình kiểm tra DOM thường đủ để khiến nó nhận ra lỗi theo cách của mình và vẽ lại chính xác, do đó, có thể chứng minh rằng việc đánh dấu là tốt. Điều này xảy ra thường xuyên (và có thể dự đoán) trong một dự án tôi đang thực hiện rằng tôi đã đặt mã tại chỗ để buộc vẽ lại trong một số trường hợp nhất định.

Điều này hoạt động trong hầu hết các kết hợp trình duyệt / os:

    el.style.cssText += ';-webkit-transform:rotateZ(0deg)'
    el.offsetHeight
    el.style.cssText += ';-webkit-transform:none'

Như trong, điều chỉnh một số thuộc tính CSS không được sử dụng, sau đó yêu cầu một số thông tin buộc vẽ lại, sau đó gỡ bỏ thuộc tính. Thật không may, đội ngũ sáng giá đằng sau Chrome cho Mac dường như đã tìm ra cách để có được sự bù đắp đó mà không cần vẽ lại. Do đó, giết một hack hữu ích khác.

Cho đến nay, điều tốt nhất tôi nghĩ ra để có được hiệu ứng tương tự trên Chrome / Mac là phần xấu xí này:

    $(el).css("border", "solid 1px transparent");
    setTimeout(function()
    {
        $(el).css("border", "solid 0px transparent");
    }, 1000);

Như trong, thực sự buộc phần tử nhảy một chút, sau đó làm lạnh một giây và nhảy trở lại. Làm cho nó tệ hơn, nếu bạn giảm thời gian chờ đó xuống dưới 500ms (đến nơi nó sẽ ít được chú ý hơn), nó thường sẽ không có hiệu ứng mong muốn, vì trình duyệt sẽ không quay lại để vẽ lại trước khi nó trở lại trạng thái ban đầu.

Bất cứ ai quan tâm để cung cấp một phiên bản tốt hơn của bản vẽ lại / làm mới này (tốt nhất là dựa trên ví dụ đầu tiên ở trên) hoạt động trên Chrome / Mac?


Tôi đã gặp vấn đề tương tự một vài phút trước đây. Tôi thay đổi thành phần cho div (đó là một nhịp) và bây giờ trình duyệt vẽ lại các thay đổi một cách chính xác. Tôi biết điều này là một chút cũ nhưng điều này có thể giúp một số bros ngoài đó tôi nghĩ.
Andrés Torres

2
Vui lòng xem câu trả lời dưới đây liên quan đến độ mờ đục 0,99 - câu trả lời tốt nhất ở đây - nhưng không dễ tìm vì nó quá sâu trên trang.
Grouchal

1
Điều này cũng xảy ra trên Linux :-(
schlingel

1
Bạn có thể thay thế thời gian chờ bằng một requestAnimationFrame, trong trường hợp đó bạn sẽ đạt được điều tương tự nhưng với độ trễ 16ms thay vì 1000ms.
trusktr

3
Vui lòng gửi sự cố Chromium cho kết xuất không hợp lệ. Có vẻ như không ai đã làm như vậy, mặc dù điều này đã được 4 năm trước.
Bardi Harborow

Câu trả lời:


183

Không chắc chắn chính xác những gì bạn đang cố gắng đạt được nhưng đây là một phương pháp tôi đã sử dụng thành công trong quá khứ để buộc trình duyệt vẽ lại, có thể nó sẽ hiệu quả với bạn.

// in jquery
$('#parentOfElementToBeRedrawn').hide().show(0);

// in plain js
document.getElementById('parentOfElementToBeRedrawn').style.display = 'none';
document.getElementById('parentOfElementToBeRedrawn').style.display = 'block';

Nếu việc vẽ lại đơn giản này không hiệu quả, bạn có thể thử cái này. Nó chèn một nút văn bản trống vào phần tử đảm bảo vẽ lại.

var forceRedraw = function(element){

    if (!element) { return; }

    var n = document.createTextNode(' ');
    var disp = element.style.display;  // don't worry about previous display style

    element.appendChild(n);
    element.style.display = 'none';

    setTimeout(function(){
        element.style.display = disp;
        n.parentNode.removeChild(n);
    },20); // you can play with this timeout to make it as short as possible
}

EDIT: Để đáp ứng với Vidime Vidas, những gì chúng ta đang đạt được ở đây sẽ là một trào lưu cưỡng bức. Bạn có thể tìm hiểu thêm từ chính chủ http://paulirish.com/2011/dom-html5-css3-performance/


3
Afaik, không thể vẽ lại chỉ một phần của khung nhìn. Trình duyệt luôn vẽ lại toàn bộ trang web.
Vidime Vidas

38
thay vì $ ('# ParentOfEuityToBeRedrawn'). hide (). show (); Tôi cần. Leather.show (0) để hoạt động chính xác trong Chrome
l.poellabauer

1
@ l.poellabauer giống nhau ở đây - điều này vô nghĩa ... nhưng cảm ơn. Làm thế quái nào bạn tìm ra ??? :)
JohnIdol

6
FYI, khu vực liên quan tôi đang làm việc là một danh sách cuộn vô hạn. Có một chút không khả thi để ẩn / hiển thị toàn bộ UL, vì vậy tôi đã chơi xung quanh và thấy rằng nếu bạn làm .hide().show(0)trên bất kỳ yếu tố nào (tôi đã chọn chân trang) thì nó sẽ làm mới trang.
cây

1
Có thể vẽ lại chỉ một phần của khung nhìn. Không có API để làm như vậy, nhưng trình duyệt có thể chọn làm như vậy. Khung nhìn được sơn trong gạch để bắt đầu. Và các lớp composite được sơn độc lập.
thêm

62

Không có câu trả lời nào ở trên làm việc cho tôi. Tôi đã nhận thấy rằng thay đổi kích thước cửa sổ của tôi đã gây ra vẽ lại. Vì vậy, điều này đã làm điều đó cho tôi:

$(window).trigger('resize');

11
gợi ý: điều này sẽ vẽ lại cửa sổ hoàn chỉnh của bạn, đó là hiệu suất tốn kém và có thể không phải là điều OP muốn
hereandnow78

7
Thay đổi kích thước cửa sổ luôn kích hoạt một dòng chỉnh lại nhưng mã $(window).trigger('resize')không, nó chỉ ném sự kiện 'thay đổi kích thước' kích hoạt trình xử lý liên quan nếu nó được gán.
guari

1
Bạn tiết kiệm cho tôi cả tuần! Vấn đề của tôi là sau khi cài đặt <body style = "zoom: 67%">, trang được phóng to đến mức mong đợi của tôi, nhưng trang bị đóng băng và không thể cuộn!. Câu trả lời của bạn đã giải quyết vấn đề này ngay lập tức
user1143669

30

Gần đây chúng tôi đã gặp phải điều này và phát hiện ra rằng việc quảng bá phần tử bị ảnh hưởng lên một lớp tổng hợp với translZ đã khắc phục vấn đề mà không cần thêm javascript.

.willnotrender { 
   transform: translateZ(0); 
}

Vì các sự cố vẽ này xuất hiện chủ yếu trong Webkit / Blink và cách khắc phục này chủ yếu nhắm vào Webkit / Blink, nên trong một số trường hợp, nó thích hợp hơn. Đặc biệt là vì nhiều giải pháp JavaScript gây ra phản xạ và sơn lại , không chỉ là sơn lại.


1
điều này hoạt động tốt với tôi khi vấn đề là một phần tử HTML đã bị xóa được vẽ trên một khung vẽ mặc dù canvas được hoạt hình liên tục.
Knaģis

Điều này đã khắc phục một vấn đề bố trí tôi gặp phải neon-animated-pages. Cảm ơn bạn: +1:
coffeesaga

1
Điều này đã làm việc. Safari đã để các yếu tố mà trong đó một bộ div không hiển thị, không hiển thị và không thể chọn. Thay đổi kích thước cửa sổ làm cho chúng biến mất. Việc thêm biến đổi này đã khiến "cổ vật" biến mất như mong đợi.
Jeff Mattson

28

Giải pháp này không có thời gian chờ! Thực lực vẽ lại! Dành cho Android và iOS.

var forceRedraw = function(element){
  var disp = element.style.display;
  element.style.display = 'none';
  var trick = element.offsetHeight;
  element.style.display = disp;
};

1
Điều này không hoạt động đối với tôi trên Chrome cũng như FF trên Linux. Tuy nhiên, chỉ cần truy cập Element.offsetHeight (không sửa đổi thuộc tính hiển thị) sẽ hoạt động. Trình duyệt sẽ bỏ qua phép tính offsetHeight khi phần tử không hiển thị.
Grodriguez

Giải pháp này hoạt động hoàn hảo để khắc phục lỗi tôi gặp phải trong trình duyệt dựa trên chrome được sử dụng trong "overwolf". Tôi đã cố gắng tìm ra cách để làm điều này và bạn đã tiết kiệm cho tôi những nỗ lực.
nacitar Sevaht

13

Điều này làm việc cho tôi. Kudos đi đây .

jQuery.fn.redraw = function() {
    return this.hide(0, function() {
        $(this).show();
    });
};

$(el).redraw();

1
Nhưng nó để lại một số 'vẫn' như 'hiển thị: chặn', v.v ... đôi khi có thể phá hủy cấu trúc trang.
pie6k

Điều này về cơ bản phải giống như$().hide().show(0)
Mahn

@Mahn bạn đã thử chưa? Theo suy đoán của tôi. Suede () là không chặn, do đó, nó sẽ bỏ qua quá trình kết xuất lại trong trình duyệt hay còn gọi là toàn bộ điểm của phương thức. (Và nếu tôi nhớ chính xác, tôi đã kiểm tra lại sau đó.)
zupa

2
@zupa nó kiểm tra và làm việc trên Chrome 38. Bí quyết là rằng show()là khác nhau từ show(0)trong đó bằng cách xác định một khoảng thời gian trong trường hợp sau, nó được kích hoạt bằng các phương pháp hoạt hình jQuery trong một thời gian chờ đúng hơn là ngay lập tức mang đến cho thời gian để render trong cùng một cách mà hide(0, function() { $(this).show(); })không .
Mahn

@Mahn ổn bắt tốt rồi. Tôi chắc chắn cho giải pháp của bạn nếu nó hoạt động đa nền tảng và không phải là một tính năng gần đây của jQuery. Vì tôi không có thời gian để kiểm tra, tôi sẽ thêm nó dưới dạng 'có thể hoạt động'. Bạn có thể chỉnh sửa câu trả lời nếu bạn nghĩ rằng áp dụng ở trên.
zupa

9

Ẩn một phần tử và sau đó hiển thị lại nó trong setTimeout 0 sẽ buộc vẽ lại.

$('#page').hide();
setTimeout(function() {
    $('#page').show();
}, 0);

1
Cái này hoạt động cho một lỗi Chrome trong đó bộ lọc SVG đôi khi không sơn lại, bug.chromium.org/p/chromium/issues/detail?id=231560 . Thời gian chờ là rất quan trọng.
Jason D

Đây là công cụ phù hợp với tôi trong khi cố gắng buộc Chrome tính toán lại các giá trị myElement.getBoundingClientRect()đang được lưu trong bộ nhớ cache, có khả năng cho mục đích tối ưu hóa / hiệu suất. Tôi chỉ đơn giản là thêm thời gian chờ 0 mili giây cho hàm thêm phần tử mới vào DOM và nhận các giá trị mới sau khi phần tử cũ có giá trị (được lưu trong bộ nhớ cache) trước đó bị xóa khỏi DOM.
TheDarkIn1978

6

Điều này dường như để làm cho tôi lừa Thêm vào đó, nó thực sự không hiển thị ở tất cả.

$(el).css("opacity", .99);
setTimeout(function(){
   $(el).css("opacity", 1);
},20);

Nó hoạt động với tôi trong Chromium Phiên bản 60.0.3112.113 liên quan đến lỗi này: Vấn đề 727076 . Hiệu quả và tinh tế; cảm ơn nhiều.
Lonnie tốt nhất

@ wiktus239 Có thể 20 millis là quá nhanh và trình duyệt không có thời gian để thay đổi thành 0,99, trước khi thời gian để thay đổi trở lại 1.0? - Thủ thuật độ mờ này hoạt động với tôi bằng mọi cách, để làm cho nội dung trong iframe hiển thị trong iPhone iOS 12 Safari và tôi chuyển đổi cứ sau 1 000 mili. Đó cũng là những gì trong vấn đề được liên kết 727076 trong nhận xét trên (1000 millis).
KajMagnus

4

cuộc gọi window.getComputedStyle()nên buộc một dòng chảy lại


2
không làm việc cho tôi, nhưng đó có lẽ là vì tôi cần offsetHeighttài sản không phải là css.
Sabas

3

Tôi đã gặp thử thách này ngày hôm nay trong OSX El Capitan với Chrome v51. Trang được đề cập hoạt động tốt trong Safari. Tôi đã thử gần như mọi đề xuất trên trang này - không có tác dụng nào đúng - tất cả đều có tác dụng phụ ... Tôi cuối cùng đã thực hiện mã bên dưới - siêu đơn giản - không có tác dụng phụ (vẫn hoạt động như trước trong Safari).

Giải pháp: Chuyển đổi một lớp về phần tử có vấn đề khi cần. Mỗi lần chuyển đổi sẽ buộc vẽ lại. (Tôi đã sử dụng jQuery để thuận tiện, nhưng JavaScript vanilla sẽ không có vấn đề gì ...)

Chuyển đổi lớp jQuery

$('.slide.force').toggleClass('force-redraw');

Lớp CSS

.force-redraw::before { content: "" }

Và đó là ...

LƯU Ý: Bạn phải chạy đoạn trích bên dưới "Toàn trang" để xem hiệu ứng.

$(window).resize(function() {
  $('.slide.force').toggleClass('force-redraw');
});
.force-redraw::before {
  content: "";
}
html,
body {
  height: 100%;
  width: 100%;
  overflow: hidden;
}
.slide-container {
  width: 100%;
  height: 100%;
  overflow-x: scroll;
  overflow-y: hidden;
  white-space: nowrap;
  padding-left: 10%;
  padding-right: 5%;
}
.slide {
  position: relative;
  display: inline-block;
  height: 30%;
  border: 1px solid green;
}
.slide-sizer {
  height: 160%;
  pointer-events: none;
  //border: 1px solid red;

}
.slide-contents {
  position: absolute;
  top: 10%;
  left: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>
  This sample code is a simple style-based solution to maintain aspect ratio of an element based on a dynamic height.  As you increase and decrease the window height, the elements should follow and the width should follow in turn to maintain the aspect ratio.  You will notice that in Chrome on OSX (at least), the "Not Forced" element does not maintain a proper ratio.
</p>
<div class="slide-container">
  <div class="slide">
    <img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    <div class="slide-contents">
      Not Forced
    </div>
  </div>
  <div class="slide force">
    <img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    <div class="slide-contents">
      Forced
    </div>
  </div>
</div>


2
function resizeWindow(){
    var evt = document.createEvent('UIEvents');
    evt.initUIEvent('resize', true, false,window,0);
    window.dispatchEvent(evt); 
}

gọi hàm này sau 500 mili giây.


2

Nếu bạn muốn giữ các kiểu được khai báo trong biểu định kiểu của mình, tốt hơn là sử dụng một cái gì đó như:

jQuery.fn.redraw = function() {
    this.css('display', 'none'); 
    var temp = this[0].offsetHeight;
    this.css('display', '');
    temp = this[0].offsetHeight;
};

$('.layer-to-repaint').redraw();

NB: với bộ webkit / nháy mới nhất, việc lưu trữ giá trị offsetHeightlà bắt buộc để kích hoạt việc sơn lại, nếu không VM (cho mục đích tối ưu hóa) có thể sẽ bỏ qua hướng dẫn đó.

Cập nhật: đã thêm lần offsetHeightđọc thứ hai , cần ngăn trình duyệt xếp hàng / lưu vào bộ đệm một thay đổi thuộc tính / lớp CSS sau với khôi phục displaygiá trị (theo cách này có thể chuyển đổi CSS có thể theo sau)


1

Mẫu Html:

<section id="parent">
  <article class="child"></article>
  <article class="child"></article>
</section>

Js:

  jQuery.fn.redraw = function() {
        return this.hide(0,function() {$(this).show(100);});
        // hide immediately and show with 100ms duration

    };

chức năng gọi:

$('article.child').redraw(); // <== ý tưởng tồi

$('#parent').redraw();

cũng hoạt động với .fadeIn(1)thay vì .show(300)cho tôi - không đẩy yếu tố xung quanh. Vì vậy, tôi đoán, đó là sự kích hoạt display:nonevà quay trở lạiblock
BananaAcid 10/03/2015

0

Một cách tiếp cận hiệu quả với tôi trên IE (Tôi không thể sử dụng kỹ thuật hiển thị vì có một đầu vào không được mất tiêu điểm)

Nó hoạt động nếu bạn có 0 lề (thay đổi phần đệm cũng hoạt động)

if(div.style.marginLeft == '0px'){
    div.style.marginLeft = '';
    div.style.marginRight = '0px';
} else {
    div.style.marginLeft = '0px';
    div.style.marginRight = '';
}

0

Chỉ CSS. Điều này hoạt động cho các tình huống trong đó một phần tử con được loại bỏ hoặc thêm vào. Trong những tình huống này, đường viền và góc tròn có thể để lại hiện vật.

el:after { content: " "; }
el:before { content: " "; }

0

Bản sửa lỗi của tôi cho IE10 + IE11. Về cơ bản những gì xảy ra là bạn thêm một DIV trong một phần tử gói phải được tính toán lại. Sau đó, chỉ cần loại bỏ nó và thì đấy; hoạt động như một bùa mê :)

    _initForceBrowserRepaint: function() {
        $('#wrapper').append('<div style="width=100%" id="dummydiv"></div>');
        $('#dummydiv').width(function() { return $(this).width() - 1; }).width(function() { return $(this).width() + 1; });
        $('#dummydiv').remove();
    },

0

Hầu hết các câu trả lời yêu cầu sử dụng thời gian chờ không đồng bộ, gây ra nháy mắt khó chịu.

Nhưng tôi đã đưa ra cái này, nó hoạt động trơn tru vì nó đồng bộ:

var p = el.parentNode,
    s = el.nextSibling;
p.removeChild(el);
p.insertBefore(el, s);

Bất kỳ sự kiện gắn liền với các yếu tố này vẫn còn hoạt động?
Kerry Johnson

0

Đây là giải pháp của tôi đã làm việc để biến mất nội dung ...

<script type = 'text/javascript'>
    var trash_div;

    setInterval(function()
    {
        if (document.body)
        {
            if (!trash_div)
                trash_div = document.createElement('div');

            document.body.appendChild(trash_div);
            document.body.removeChild(trash_div);
        }
    }, 1000 / 25); //25 fps...
</script>

0

Tôi gặp phải một vấn đề tương tự và dòng JS đơn giản này đã giúp khắc phục nó:

document.getElementsByTagName('body')[0].focus();

Trong trường hợp của tôi, đó là một lỗi với tiện ích mở rộng Chrome không vẽ lại trang sau khi thay đổi CSS từ bên trong tiện ích mở rộng.


4
Đây là phá vỡ khả năng truy cập bàn phím.
Pangamma

0

Tôi muốn trả lại tất cả các trạng thái về trạng thái trước đó (không tải lại) bao gồm các yếu tố được thêm bởi jquery. Việc thực hiện ở trên sẽ không hoạt động. và tôi đã làm như sau.

// Set initial HTML description
var defaultHTML;
function DefaultSave() {
  defaultHTML = document.body.innerHTML;
}
// Restore HTML description to initial state
function HTMLRestore() {
  document.body.innerHTML = defaultHTML;
}



DefaultSave()
<input type="button" value="Restore" onclick="HTMLRestore()">

0

Tôi đã có một danh sách thành phần phản ứng mà khi cuộn, sau đó mở một trang khác, sau đó khi quay lại danh sách không được hiển thị trên Safari iOS cho đến khi cuộn trang. Vì vậy, đây là sửa chữa.

    componentDidMount() {
        setTimeout(() => {
            window.scrollBy(0, 0);
        }, 300);
    }

0

Dưới đây css hoạt động với tôi trên IE 11 và Edge, không cần sử dụng JS. scaleY(1)lừa ở đây Có vẻ là giải pháp đơn giản nhất.

.box {
    max-height: 360px;
    transition: all 0.3s ease-out;
    transform: scaleY(1);
}
.box.collapse {
    max-height: 0;
}

0

Nó đã giúp đỡ tôi

domNodeToRerender.style.opacity = 0.99;
setTimeout(() => { domNodeToRerender.style.opacity = '' }, 0);

0

2020: Nhẹ hơn và mạnh hơn

Các giải pháp trước đây không còn hiệu quả với tôi nữa.

Tôi đoán các trình duyệt tối ưu hóa quá trình vẽ bằng cách phát hiện ngày càng nhiều thay đổi "vô dụng".

Giải pháp này làm cho trình duyệt vẽ một bản sao để thay thế phần tử ban đầu. Nó hoạt động và có lẽ bền vững hơn:

const element = document.querySelector('selector');
if (element ) {
  const clone = element.cloneNode(true);
  element.replaceWith(clone);
}

đã thử nghiệm trên Chrome 80 / Edge 80 / Firefox 75

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.