菜单打开时,只应滚动侧边菜单div和其他菜单

时间:2017-03-14 03:16:33

标签: css reactjs scroll overflow css-position

我有一个侧边菜单,当它打开时,身体可以被部分看到。我的侧边菜单可能很长,所以你可以滚动它。但是当菜单位于底部时,你会滚动身体,我不想要这种行为。

Scrolling only content div, others should be fixed类似,但我使用的是React。当我的侧边菜单关闭时,其他内容应该是可滚动的。将内容视为链接示例中的侧边菜单。到目前为止,我使用了该答案提供的相同技术,但它很丑陋(有点jQuery):

preventOverflow = (menuOpen) => { // this is called when side menu is toggled
    const body = document.getElementsByTagName('body')[0]; // this should be fixed when side menu is open

    if (menuOpen) {
        body.className += ' overflow-hidden';
    } else {
        body.className = body.className.replace(' overflow-hidden', '');
    }
}

// css
.overflow-hidden {
    overflow-y: hidden;
}

我应该如何处理Reactjs?

2 个答案:

答案 0 :(得分:0)

ReactJS无法直接访问<body>元素,而这是需要更改其overflow-y样式的元素。因此,虽然你所做的不是最漂亮的代码,但它也不是完全错误的。

我给出的唯一真实建议是( shudder )在主体上使用内联样式而不是类名,以避免引入CSS声明。只要您的菜单是唯一负责更新overflow-y属性的菜单,就没有理由不能在其上使用内联样式。使用?:运算符对其进行混搭会产生相当简单的代码:

body.style.overflowY = menuOpen ? "hidden" : "";

然后您可以完全删除.overflow-hidden类。

如果出于某种原因,多个事情正在管理正文的溢出状态,那么您可能希望坚持使用类名并为管理它的每个事物分配一个唯一的类,如下所示:

if (menuOpen) {
    body.className += ' menu-open';
}
else {
    // Use some tricks from jQuery to remove the "menu-open" class more elegantly.
    var className = " " + body.className + " ";
    className = className.replace(" overflow-hidden ", " ").replace(/\s+/, " ");
    className = className.substr(1, className.length - 2);     
}

CSS:

body.menu-open {
    overflow-y: hidden;
}

答案 1 :(得分:0)

你应该让meta组件做出反应来改变身体上的东西,以及更改文档标题等内容。我不久前做了一个为我做这件事。我会在这里添加它。

用法

render() {
    return (
        <div>
             <DocumentMeta bodyClasses={[isMenuOpen ? 'no-scroll' : '']} />
            ... rest of your normal code
        </div>
    )
}

DocumentMeta.jsx

import React from 'react';
import _ from 'lodash';
import withSideEffect from 'react-side-effect';

var HEADER_ATTRIBUTE = "data-react-header";
var TAG_NAMES = {
  META: "meta",
  LINK: "link",
};
var TAG_PROPERTIES = {
  NAME: "name",
  CHARSET: "charset",
  HTTPEQUIV: "http-equiv",
  REL: "rel",
  HREF: "href",
  PROPERTY: "property",
  CONTENT: "content"
};
var getInnermostProperty = (propsList, property) => {
  return _.result(_.find(propsList.reverse(), property), property);
};

var getTitleFromPropsList = (propsList) => {
  var innermostTitle = getInnermostProperty(propsList, "title");
  var innermostTemplate = getInnermostProperty(propsList, "titleTemplate");
  if (innermostTemplate && innermostTitle) {
      return innermostTemplate.replace(/\%s/g, innermostTitle);
  }
  return innermostTitle || "";
};
var getBodyIdFromPropsList = (propsList) => {
  var bodyId = getInnermostProperty(propsList, "bodyId");
  return bodyId;
};
var getBodyClassesFromPropsList = (propsList) => {
  return propsList
    .filter(props => props.bodyClasses && Array.isArray(props.bodyClasses))
    .map(props => props.bodyClasses)
    .reduce((classes, list) => classes.concat(list), []);
};
var getTagsFromPropsList = (tagName, uniqueTagIds, propsList) => {
  // Calculate list of tags, giving priority innermost component (end of the propslist)
  var approvedSeenTags = {};
  var validTags = _.keys(TAG_PROPERTIES).map(key => TAG_PROPERTIES[key]);

  var tagList = propsList
    .filter(props => props[tagName] !== undefined)
    .map(props => props[tagName])
    .reverse()
    .reduce((approvedTags, instanceTags) => {
      var instanceSeenTags = {};

      instanceTags.filter(tag => {
        for(var attributeKey in tag) {
          var value = tag[attributeKey].toLowerCase();
          var attributeKey = attributeKey.toLowerCase();

          if (validTags.indexOf(attributeKey) == -1) {
              return false;
          }
          if (!approvedSeenTags[attributeKey]) {
              approvedSeenTags[attributeKey] = [];
          }
          if (!instanceSeenTags[attributeKey]) {
              instanceSeenTags[attributeKey] = [];
          }

          if (!_.has(approvedSeenTags[attributeKey], value)) {
              instanceSeenTags[attributeKey].push(value);
              return true;
          }
          return false;
        }
      })
      .reverse()
      .forEach(tag => approvedTags.push(tag));

      // Update seen tags with tags from this instance
      _.keys(instanceSeenTags).forEach((attr) => {
        approvedSeenTags[attr] = _.union(approvedSeenTags[attr], instanceSeenTags[attr])
      });
      instanceSeenTags = {};
      return approvedTags;
    }, []);
  return tagList;
};
var updateTitle = title => {
  document.title = title || document.title;
};
var updateBodyId = (id) => {
  document.body.setAttribute("id", id);
};
var updateBodyClasses = classes => {
  document.body.className = "";
  classes.forEach(cl => {
    if(!cl || cl == "") return;
    document.body.classList.add(cl);
  });
};
var updateTags = (type, tags) => {
  var headElement = document.head || document.querySelector("head");
  var existingTags = headElement.querySelectorAll(`${type}[${HEADER_ATTRIBUTE}]`);
  existingTags = Array.prototype.slice.call(existingTags);
  // Remove any duplicate tags
  existingTags.forEach(tag => tag.parentNode.removeChild(tag));

  if (tags && tags.length) {
    tags.forEach(tag => {
      var newElement = document.createElement(type);
      for (var attribute in tag) {
        if (tag.hasOwnProperty(attribute)) {
            newElement.setAttribute(attribute, tag[attribute]);
        }
      }
      newElement.setAttribute(HEADER_ATTRIBUTE, "true");
      headElement.insertBefore(newElement, headElement.firstChild);
    });
  }
};
var generateTagsAsString = (type, tags) => {
  var html = tags.map(tag => {
  var attributeHtml = Object.keys(tag)
    .map((attribute) => {
        const encodedValue = HTMLEntities.encode(tag[attribute], {
            useNamedReferences: true
        });
        return `${attribute}="${encodedValue}"`;
    })
    .join(" ");
    return `<${type} ${attributeHtml} ${HEADER_ATTRIBUTE}="true" />`;
  });
  return html.join("\n");
};

var reducePropsToState = (propsList) => ({
  title: getTitleFromPropsList(propsList),
  metaTags: getTagsFromPropsList(TAG_NAMES.META, [TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.CONTENT], propsList),
  linkTags: getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList),
  bodyId: getBodyIdFromPropsList(propsList),
  bodyClasses: getBodyClassesFromPropsList(propsList),
});
var handleClientStateChange = ({title, metaTags, linkTags, bodyId, bodyClasses}) => {
  updateTitle(title);
  updateTags(TAG_NAMES.LINK, linkTags);
  updateTags(TAG_NAMES.META, metaTags);
  updateBodyId(bodyId);
  updateBodyClasses(bodyClasses)
};
var mapStateOnServer = ({title, metaTags, linkTags}) => ({
  title: HTMLEntities.encode(title),
  meta: generateTagsAsString(TAG_NAMES.META, metaTags),
  link: generateTagsAsString(TAG_NAMES.LINK, linkTags)
});

var DocumentMeta = React.createClass({
  propTypes: {
    title: React.PropTypes.string,
    titleTemplate: React.PropTypes.string,
    meta: React.PropTypes.arrayOf(React.PropTypes.object),
    link: React.PropTypes.arrayOf(React.PropTypes.object),
    children: React.PropTypes.oneOfType([
        React.PropTypes.object,
        React.PropTypes.array
    ]),
    bodyClasses: React.PropTypes.array,
  },
  render() {
    if (Object.is(React.Children.count(this.props.children), 1)) {
        return React.Children.only(this.props.children);
    } else if (React.Children.count(this.props.children) > 1) {
        return (
            <span>
                {this.props.children}
            </span>
        );
    }
    return null;
  },
});
DocumentMeta = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(DocumentMeta);
module.exports = DocumentMeta;

对于你的情况,这个组件可能会稍微改变一下(withSideEffect用于客户端和服务器端渲染......如果你不使用服务器端渲染,那么它可能不是完全必要的)但是该组件可以在客户端工作渲染,如果你想在那里使用它。