การตั้งค่ากฎคลาสหลอก CSS จาก JavaScript


125

ฉันกำลังหาวิธีเปลี่ยนกฎ CSS สำหรับตัวเลือกคลาสหลอก (เช่น: link,: hover ฯลฯ ) จาก JavaScript

ดังนั้นอะนาล็อกของโค้ด CSS: a:hover { color: red }ใน JS

ฉันไม่พบคำตอบจากที่อื่น หากใครรู้ว่านี่เป็นสิ่งที่เบราว์เซอร์ไม่รองรับนั่นก็จะเป็นประโยชน์เช่นกัน

คำตอบ:


192

คุณไม่สามารถจัดสไตล์คลาสหลอกในองค์ประกอบเฉพาะเพียงอย่างเดียวในลักษณะเดียวกับที่คุณไม่สามารถมีคลาสหลอกในแอตทริบิวต์ inline style = "... " (เนื่องจากไม่มีตัวเลือก)

คุณสามารถทำได้โดยการแก้ไขสไตล์ชีตเช่นเพิ่มกฎ:

#elid:hover { background: red; }

สมมติว่าแต่ละองค์ประกอบที่คุณต้องการให้มีผลมี ID เฉพาะเพื่อให้สามารถเลือกได้

ในทางทฤษฎีเอกสารที่คุณต้องการคือhttp://www.w3.org/TR/DOM-Level-2-Style/Overview.htmlซึ่งหมายความว่าคุณสามารถ (กำหนดสไตล์ชีตแบบฝังหรือลิงก์ที่มีอยู่แล้ว) โดยใช้ไวยากรณ์เช่น:

document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';

แน่นอนว่า IE ต้องการไวยากรณ์ของตัวเอง:

document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';

เบราว์เซอร์รุ่นเก่าและรุ่นรองมีแนวโน้มที่จะไม่รองรับทั้งไวยากรณ์ สไตล์ชีตแบบไดนามิกไม่ค่อยทำเพราะมันค่อนข้างน่ารำคาญในการทำให้ถูกต้องไม่ค่อยจำเป็นและเป็นปัญหาในอดีต


32
เหตุใดจึงไม่ได้รับเลือกให้เป็นคำตอบ
SZH


2
Firefox: "ข้อผิดพลาด: การดำเนินการไม่ปลอดภัย"
8128

@fluteflute การดำเนินการถือว่าไม่ปลอดภัยหากคุณกำลังพยายามจัดการไฟล์ CSS จากโดเมนอื่น (ฉันเดาว่ามันเป็นนโยบายประเภทเดียวกัน) ความอัปยศ! ฟังก์ชั่นง่ายๆในการตรวจสอบความสอดคล้องกับนโยบายแหล่งกำเนิดเดียวกัน: function sameOrigin(url) { var loc = window.location, a = document.createElement('a'); a.href = url; return a.hostname === loc.hostname && a.port === loc.port && a.protocol === loc.protocol; }
WickyNilliams

3
ไม่แนะนำให้ใช้การจัดการสไตล์ชีตเพื่อใช้สไตล์กับองค์ประกอบเฉพาะเนื่องจากจะทำให้เบราว์เซอร์แสดงเอกสารทั้งหมดทุกครั้งที่มีการเปลี่ยนแปลงสไตล์ชีต Stackella.org/content/2009/03/27/…
Johannes Ewald

29

ฉันรวบรวมไลบรารีเล็ก ๆไว้ด้วยกันเนื่องจากฉันคิดว่ามีกรณีการใช้งานที่ถูกต้องสำหรับการจัดการสไตล์ชีตใน JS เหตุผลคือ:

  • การตั้งค่ารูปแบบที่ต้องคำนวณหรือเรียกค้น - ตัวอย่างเช่นการตั้งค่าขนาดฟอนต์ที่ผู้ใช้ต้องการจากคุกกี้
  • การตั้งค่าลักษณะพฤติกรรม (ไม่ใช่ความสวยงาม) โดยเฉพาะอย่างยิ่งสำหรับนักพัฒนาวิดเจ็ต / ปลั๊กอิน UI แท็บภาพหมุนและอื่น ๆ มักต้องการ CSS พื้นฐานเพื่อให้ทำงานได้ไม่ควรต้องการสไตล์ชีตสำหรับฟังก์ชันหลัก
  • ดีกว่ารูปแบบอินไลน์เนื่องจากกฎ CSS ใช้กับองค์ประกอบทั้งหมดในปัจจุบันและอนาคตและไม่ทำให้ HTML รกเมื่อดูใน Firebug / Developer Tools

17

ฟังก์ชั่นสำหรับรับมือกับสิ่งต่างๆข้ามเบราว์เซอร์:

addCssRule = function(/* string */ selector, /* string */ rule) {
  if (document.styleSheets) {
    if (!document.styleSheets.length) {
      var head = document.getElementsByTagName('head')[0];
      head.appendChild(bc.createEl('style'));
    }

    var i = document.styleSheets.length-1;
    var ss = document.styleSheets[i];

    var l=0;
    if (ss.cssRules) {
      l = ss.cssRules.length;
    } else if (ss.rules) {
      // IE
      l = ss.rules.length;
    }

    if (ss.insertRule) {
      ss.insertRule(selector + ' {' + rule + '}', l);
    } else if (ss.addRule) {
      // IE
      ss.addRule(selector, rule, l);
    }
  }
};

9

เพียงวาง css ในสตริงเทมเพลต

const cssTemplateString = `.foo:[psuedoSelector]{prop: value}`;

จากนั้นสร้างองค์ประกอบสไตล์และวางสตริงในแท็กสไตล์แล้วแนบเข้ากับเอกสาร

const styleTag = document.createElement("style");
styleTag.innerHTML = cssTemplateString;
document.head.insertAdjacentElement('beforeend', styleTag);

ความจำเพาะจะดูแลส่วนที่เหลือ จากนั้นคุณสามารถลบและเพิ่มแท็กสไตล์แบบไดนามิก นี่เป็นทางเลือกง่ายๆสำหรับไลบรารีและยุ่งกับอาร์เรย์สไตล์ชีทใน DOM Happy Coding!


insertAdjacentElementต้องการอาร์กิวเมนต์ 2 ตัวในเบราว์เซอร์หลัก (Chrome, Firefox, Edge) ซึ่งตัวแรกสร้างตำแหน่งที่สัมพันธ์กับองค์ประกอบอ้างอิง ( ดูที่นี่ ) นี่เป็นการเปลี่ยนแปลงล่าสุดหรือไม่ (เพิ่ม 'beforeend' ในคำตอบ)
ถล่ม

6

เคล็ดลับของฉันคือการใช้ตัวเลือกแอตทริบิวต์ แอตทริบิวต์นั้นง่ายต่อการตั้งค่าโดยใช้จาวาสคริปต์

CSS

.class{ /*normal css... */}
.class[special]:after{ content: 'what you want'}

จาวาสคริปต์

  function setSpecial(id){ document.getElementById(id).setAttribute('special', '1'); }

HTML

<element id='x' onclick="setSpecial(this.id)"> ...  

4
โซลูชันนี้ใช้ jQuery - การแนะนำขนาดของ jQuery สำหรับสิ่งที่ง่ายเช่นนี้เมื่อผู้ถามถามหา Javascript ที่บริสุทธิ์นั้นไม่ดี
Tom Ashworth

แม้ว่าในปัจจุบันเว็บไซต์ทั้งหมดจะใช้ jquery แต่ฉันจะแก้ไขให้ใช้ javascript บริสุทธิ์
Sergio Abreu

1
วิธีนี้จะเปลี่ยนแอตทริบิวต์ CSS ของ. class [พิเศษ] ได้อย่างไร: หลังจากองค์ประกอบหลอกเช่นสีพื้นหลังหรืออย่างอื่น?
andreszs

5

มีทางเลือกอื่น แทนที่จะจัดการกับคลาสหลอกโดยตรงให้สร้างคลาสจริงที่จำลองสิ่งเดียวกันเช่นคลาส "โฮเวอร์" หรือคลาส "เยี่ยมชม" จัดสไตล์ชั้นเรียนตามปกติ "." ไวยากรณ์จากนั้นคุณสามารถใช้ JavaScript เพื่อเพิ่มหรือลบคลาสออกจากองค์ประกอบเมื่อเหตุการณ์ที่เหมาะสมเริ่มทำงาน


2
ใช้ไม่ได้กับ: before and: after pseudo class.
jbyrd

และไม่สามารถใช้ได้กับการเปลี่ยนภาพพื้นหลังด้วยค่าที่ดึงผ่าน AJAX
andreszs

1
@jbyrd :before& :afterเป็นองค์ประกอบหลอกไม่ใช่คลาส
Roy Ling

@royling - ใช่ขอบคุณสำหรับการแก้ไข! ดูเหมือนจะแก้ไขความคิดเห็นของฉันไม่ได้
jbyrd

4

แทนที่จะตั้งกฎหลอกคลาสโดยตรงด้วยจาวาสคริปต์คุณสามารถตั้งค่ากฎที่แตกต่างกันในไฟล์ CSS ต่างๆจากนั้นใช้ Javascript เพื่อปิดสไตล์ชีทหนึ่งและเพื่อเปิดอีกอันหนึ่ง มีการอธิบายวิธีการไว้ที่A List Apart (qv. สำหรับรายละเอียดเพิ่มเติม)

ตั้งค่าไฟล์ CSS เป็น

<link rel="stylesheet" href="always_on.css">
<link rel="stylesheet" title="usual" href="preferred.css"> <!-- on by default -->
<link rel="alternate stylesheet" title="strange" href="alternate.css"> <!-- off by default -->

จากนั้นสลับไปมาระหว่างกันโดยใช้จาวาสคริปต์:

function setActiveStyleSheet(title) {
   var i, a, main;
   for(i=0; (a = document.getElementsByTagName("link")<i>); i++) {
     if(a.getAttribute("rel").indexOf("style") != -1
        && a.getAttribute("title")) {
       a.disabled = true;
       if(a.getAttribute("title") == title) a.disabled = false;
     }
   }
}

จะเกิดอะไรขึ้นถ้าคุณต้องการเปลี่ยนคลาสเป็นค่าที่เรียกโดยคำขอ AJAX คุณไม่สามารถสร้างไฟล์ CSS ได้ในขณะนี้ ...
andreszs

2

ตามที่ระบุไว้แล้วนี่ไม่ใช่สิ่งที่เบราว์เซอร์รองรับ

หากคุณไม่ได้ใช้สไตล์แบบไดนามิก (เช่นดึงออกจากฐานข้อมูลหรือบางอย่าง) คุณควรจะสามารถแก้ไขปัญหานี้ได้โดยการเพิ่มคลาสในเนื้อหาของเพจ

css จะมีลักษณะดังนี้:

a:hover { background: red; }
.theme1 a:hover { background: blue; }

และจาวาสคริปต์ที่จะเปลี่ยนสิ่งนี้จะเป็นดังนี้:

// Look up some good add/remove className code if you want to do this
// This is really simplified

document.body.className += " theme1";  

ด้วยความอยากรู้อยากเห็นelement.classList.addไม่ได้รับการสนับสนุนอย่างดี? ฉันเห็นคนทำอยู่element.className +=เรื่อย ๆ
Joel Cornett

2
classList เป็นฟีเจอร์ที่ใหม่กว่าและดูเหมือนจะไม่ได้รับการสนับสนุนที่ดีจนกระทั่งเมื่อไม่นานมานี้ (ดูcaniuse.com/classlist )
Nathaniel Reinhart


-1

ใน jquery คุณสามารถตั้งค่าคลาสหลอกแบบโฮเวอร์ได้อย่างง่ายดาย

$("p").hover(function(){
$(this).css("background-color", "yellow");
}, function(){
$(this).css("background-color", "pink");
});

-1

นี่คือโซลูชันที่มีสองฟังก์ชัน: addCSSclass เพิ่มคลาส css ใหม่ให้กับเอกสารและ toggleClass จะเปิดใช้งาน

ตัวอย่างแสดงการเพิ่มแถบเลื่อนแบบกำหนดเองไปยัง div

// If newState is provided add/remove theClass accordingly, otherwise toggle theClass
function toggleClass(elem, theClass, newState) {
  var matchRegExp = new RegExp('(?:^|\\s)' + theClass + '(?!\\S)', 'g');
  var add = (arguments.length > 2 ? newState : (elem.className.match(matchRegExp) === null));

  elem.className = elem.className.replace(matchRegExp, ''); // clear all
  if (add) elem.className += ' ' + theClass;
}

function addCSSclass(rules) {
  var style = document.createElement("style");
  style.appendChild(document.createTextNode("")); // WebKit hack :(
  document.head.appendChild(style);
  var sheet = style.sheet;

  rules.forEach((rule, index) => {
    try {
      if ("insertRule" in sheet) {
        sheet.insertRule(rule.selector + "{" + rule.rule + "}", index);
      } else if ("addRule" in sheet) {
        sheet.addRule(rule.selector, rule.rule, index);
      }
    } catch (e) {
      // firefox can break here          
    }
    
  })
}

let div = document.getElementById('mydiv');
addCSSclass([{
    selector: '.narrowScrollbar::-webkit-scrollbar',
    rule: 'width: 5px'
  },
  {
    selector: '.narrowScrollbar::-webkit-scrollbar-thumb',
    rule: 'background-color:#808080;border-radius:100px'
  }
]);
toggleClass(div, 'narrowScrollbar', true);
<div id="mydiv" style="height:300px;width:300px;border:solid;overflow-y:scroll">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus
  a diam volutpat, ullamcorper justo eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit
  nec sodales sodales. Etiam eget dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui. Lorem ipsum dolor sit amet, consectetur
  adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus a diam volutpat, ullamcorper justo
  eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit nec sodales sodales. Etiam eget
  dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui.
</div>


-2

ถ้าคุณใช้ REACT มีสิ่งที่เรียกว่าเป็นเรเดียม มีประโยชน์มากที่นี่:

  • เพิ่มตัวจัดการให้กับอุปกรณ์ประกอบฉากหากมีการระบุรูปแบบการโต้ตอบเช่น onMouseEnter for: hover, ตัดตัวจัดการที่มีอยู่หากจำเป็น

  • หากตัวจัดการใด ๆ ถูกทริกเกอร์เช่นโดยการวางเมาส์เหนือ Radium จะเรียกใช้ setState เพื่ออัปเดตฟิลด์เฉพาะเรเดียมบนวัตถุสถานะส่วนประกอบ

  • ในการแสดงผลซ้ำให้แก้ไขรูปแบบโต้ตอบที่ใช้เช่น: วางเมาส์เหนือโดยค้นหาคีย์ขององค์ประกอบหรือการอ้างอิงในสถานะเฉพาะเรเดียม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.